Compare commits
No commits in common. "master" and "compartmentalization" have entirely different histories.
master
...
compartmen
|
|
@ -1,8 +1,8 @@
|
||||||
using AS1024.GeoFeed.Core.Interfaces;
|
using AS1024.GeoFeed.Core.Interfaces;
|
||||||
using AS1024.GeoFeed.Core.Tools;
|
|
||||||
using AS1024.GeoFeed.Models;
|
using AS1024.GeoFeed.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using AS1024.GeoFeed.Core.Tools;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache
|
namespace AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache
|
||||||
{
|
{
|
||||||
|
|
@ -58,7 +58,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache
|
||||||
List<IPGeoFeed> cachedData = [];
|
List<IPGeoFeed> cachedData = [];
|
||||||
results.ForEach(cachedData.Add);
|
results.ForEach(cachedData.Add);
|
||||||
|
|
||||||
return cachedData.ToGeoFeedCsv(true, true);
|
return cachedData.ToGeoFeedCsv();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DBContextMigrate()
|
private async Task DBContextMigrate()
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Core\AS1024.GeoFeed.Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
using AS1024.GeoFeed.Core.Interfaces;
|
|
||||||
using AS1024.GeoFeed.Core.Tools;
|
|
||||||
using AS1024.GeoFeed.Models;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.Core.WebLogic
|
|
||||||
{
|
|
||||||
public class GeoFeedReturn
|
|
||||||
{
|
|
||||||
private const string GeoFeedCacheKey = "GeoFeedData";
|
|
||||||
private const string GeoFeedMimeTypeReturn = "text/csv";
|
|
||||||
private const string GeoFeedFileName = "geofeed.csv";
|
|
||||||
private readonly IGeoFeedProvider provider;
|
|
||||||
private readonly ILogger<GeoFeedReturn> logger;
|
|
||||||
private readonly IGeoFeedPersistentCacheProvider cacheProvider;
|
|
||||||
private readonly IMemoryCache memoryCache;
|
|
||||||
private readonly IWebHostEnvironment environment;
|
|
||||||
|
|
||||||
public GeoFeedReturn(IGeoFeedProvider provider,
|
|
||||||
ILogger<GeoFeedReturn> logger,
|
|
||||||
IGeoFeedPersistentCacheProvider cacheProvider,
|
|
||||||
IMemoryCache memoryCache,
|
|
||||||
IWebHostEnvironment environment)
|
|
||||||
{
|
|
||||||
this.provider = provider;
|
|
||||||
this.logger = logger;
|
|
||||||
this.cacheProvider = cacheProvider;
|
|
||||||
this.memoryCache = memoryCache;
|
|
||||||
this.environment = environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IResult> GetGeoFeed()
|
|
||||||
{
|
|
||||||
bool isCached = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!memoryCache.TryGetValue(GeoFeedCacheKey, out List<IPGeoFeed>? feed))
|
|
||||||
{
|
|
||||||
isCached = false;
|
|
||||||
feed = await provider.GetGeoFeedDataAsync();
|
|
||||||
if (environment.IsProduction())
|
|
||||||
{
|
|
||||||
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
|
|
||||||
.SetSlidingExpiration(TimeSpan.FromMinutes(15));
|
|
||||||
memoryCache.Set(GeoFeedCacheKey, feed, cacheEntryOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Results.File(Encoding.UTF8.GetBytes(feed.ToGeoFeedCsv(true, isCached)),
|
|
||||||
GeoFeedMimeTypeReturn,
|
|
||||||
GeoFeedFileName);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (HttpRequestException ex)
|
|
||||||
{
|
|
||||||
logger.LogWarning($"Temporary failure of retrieving GeoData from upstream. {ex}");
|
|
||||||
string geoFeedData = cacheProvider.GetGeoFeed();
|
|
||||||
|
|
||||||
return Results.File(Encoding.UTF8.GetBytes(geoFeedData),
|
|
||||||
GeoFeedMimeTypeReturn,
|
|
||||||
GeoFeedFileName);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError($"Error: {ex}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Results.NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"AS1024.GeoFeed.Core.WebLogic": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
},
|
|
||||||
"applicationUrl": "https://localhost:54455;http://localhost:54456"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,10 @@ namespace AS1024.GeoFeed.Core.CacheService
|
||||||
{
|
{
|
||||||
var scope = host.Services.CreateScope();
|
var scope = host.Services.CreateScope();
|
||||||
|
|
||||||
var persistentCacheProvider =
|
var persistentCacheProvider =
|
||||||
scope.ServiceProvider.GetRequiredService<IGeoFeedPersistentCacheProvider>();
|
scope.ServiceProvider.GetRequiredService<IGeoFeedPersistentCacheProvider>();
|
||||||
|
|
||||||
var results = await feedProvider.GetGeoFeedDataAsync();
|
var results = await feedProvider.GetGeoFeedData();
|
||||||
await persistentCacheProvider.CacheGeoFeed(results);
|
await persistentCacheProvider.CacheGeoFeed(results);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
using AS1024.GeoFeed.Core.Interfaces;
|
using AS1024.GeoFeed.Core.Interfaces;
|
||||||
using AS1024.GeoFeed.Core.Tools;
|
|
||||||
using AS1024.GeoFeed.Models;
|
using AS1024.GeoFeed.Models;
|
||||||
|
using AS1024.GeoFeed.Core.Tools;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.Core.CacheService
|
namespace AS1024.GeoFeed.Core.CacheService
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedPreloader
|
||||||
private async Task StartPreLoad()
|
private async Task StartPreLoad()
|
||||||
{
|
{
|
||||||
logger.LogInformation("Preloading GeoFeed data in memory...");
|
logger.LogInformation("Preloading GeoFeed data in memory...");
|
||||||
List<IPGeoFeed> feed = await provider.GetGeoFeedDataAsync();
|
List<IPGeoFeed> feed = await provider.GetGeoFeedData();
|
||||||
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(45));
|
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(45));
|
||||||
memoryCache.Set(GeoFeedCacheKey, feed, cacheEntryOptions);
|
memoryCache.Set(GeoFeedCacheKey, feed, cacheEntryOptions);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedProviders
|
||||||
{
|
{
|
||||||
public class NetBoxGeoFeedProvider : NetBoxGeoFeedProviderBase, IGeoFeedProvider
|
public class NetBoxGeoFeedProvider : NetBoxGeoFeedProviderBase, IGeoFeedProvider
|
||||||
{
|
{
|
||||||
public NetBoxGeoFeedProvider(IConfiguration configuration, ILogger<NetBoxGeoFeedProvider> logger, IHttpClientFactory httpClientFactory)
|
public NetBoxGeoFeedProvider(IConfiguration configuration, ILogger<NetBoxGeoFeedProvider> logger, IHttpClientFactory httpClientFactory)
|
||||||
: base(configuration, logger, httpClientFactory)
|
: base(configuration, logger, httpClientFactory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
using AS1024.GeoFeed.Core.Interfaces;
|
using AS1024.GeoFeed.Core.Interfaces;
|
||||||
using AS1024.GeoFeed.Models;
|
using AS1024.GeoFeed.Models;
|
||||||
using Microsoft.Extensions.Configuration;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.Core.GeoFeedProviders
|
namespace AS1024.GeoFeed.Core.GeoFeedProviders
|
||||||
{
|
{
|
||||||
|
|
@ -31,7 +31,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedProviders
|
||||||
this.httpClientFactory = httpClientFactory;
|
this.httpClientFactory = httpClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<IPGeoFeed>> GetGeoFeedDataAsync()
|
public async Task<List<IPGeoFeed>> GetGeoFeedData()
|
||||||
{
|
{
|
||||||
List<IPGeoFeed> geoFeed = [];
|
List<IPGeoFeed> geoFeed = [];
|
||||||
using HttpClient client = httpClientFactory.CreateClient();
|
using HttpClient client = httpClientFactory.CreateClient();
|
||||||
|
|
@ -52,7 +52,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedProviders
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var stringResult = await result.Content.ReadAsStreamAsync();
|
string stringResult = await result.Content.ReadAsStringAsync();
|
||||||
jsonData = DeserializeJsonData(stringResult);
|
jsonData = DeserializeJsonData(stringResult);
|
||||||
|
|
||||||
if (jsonData?.Results == null || jsonData.Results.Count == 0)
|
if (jsonData?.Results == null || jsonData.Results.Count == 0)
|
||||||
|
|
@ -93,7 +93,7 @@ namespace AS1024.GeoFeed.Core.GeoFeedProviders
|
||||||
return geoFeed;
|
return geoFeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual NetboxData? DeserializeJsonData(Stream stringResult)
|
protected virtual NetboxData? DeserializeJsonData(string stringResult)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize<NetboxData>(stringResult, new JsonSerializerOptions
|
return JsonSerializer.Deserialize<NetboxData>(stringResult, new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
using AS1024.GeoFeed.Models;
|
using AS1024.GeoFeed.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.Core.Interfaces
|
namespace AS1024.GeoFeed.Core.Interfaces
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ namespace AS1024.GeoFeed.Core.Interfaces
|
||||||
public interface IGeoFeedProvider
|
public interface IGeoFeedProvider
|
||||||
{
|
{
|
||||||
public string GeoFeedProviderName { get; }
|
public string GeoFeedProviderName { get; }
|
||||||
public Task<List<IPGeoFeed>> GetGeoFeedDataAsync();
|
public Task<List<IPGeoFeed>> GetGeoFeedData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,35 +5,16 @@ namespace AS1024.GeoFeed.Core.Tools
|
||||||
{
|
{
|
||||||
public static class GeoFeedTools
|
public static class GeoFeedTools
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static string ToGeoFeedCsv(this List<IPGeoFeed> geoFeeds)
|
||||||
/// Returns a CSV string for a given GeoFeed retrieved from various sources.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="geoFeeds">GeoFeed returned from the source of truth.</param>
|
|
||||||
/// <param name="timeStamp">If a timestamp should be appended at the header.</param>
|
|
||||||
/// <param name="isCached">If the result is cached.</param>
|
|
||||||
/// <returns>CSV formatted string of GeoFeed data.</returns>
|
|
||||||
public static string ToGeoFeedCsv(this List<IPGeoFeed> geoFeeds, bool timeStamp = false, bool isCached = false)
|
|
||||||
{
|
{
|
||||||
if (geoFeeds == null) throw new ArgumentNullException(nameof(geoFeeds));
|
|
||||||
|
|
||||||
StringBuilder csvContent = new();
|
StringBuilder csvContent = new();
|
||||||
|
|
||||||
// Append timestamp header if required
|
|
||||||
if (timeStamp)
|
|
||||||
csvContent.AppendFormat("# GeoFeed generated on {0:R}\n", DateTime.UtcNow);
|
|
||||||
|
|
||||||
// Append cache status if required
|
|
||||||
if (isCached)
|
|
||||||
csvContent.AppendLine("# Geofeed data is returned from local in memory cache");
|
|
||||||
|
|
||||||
// Iterate over each GeoFeed entry to append its details to the CSV content
|
|
||||||
foreach (IPGeoFeed feed in geoFeeds)
|
foreach (IPGeoFeed feed in geoFeeds)
|
||||||
{
|
{
|
||||||
// Using AppendFormat for a cleaner and more readable approach to constructing CSV lines
|
csvContent.AppendLine($"{feed.Prefix},{feed.GeolocCountry},{feed.GeolocRegion},{feed.GeolocCity},{feed.GeolocPostalCode}");
|
||||||
csvContent.AppendFormat("{0},{1},{2},{3},{4}\n", feed.Prefix, feed.GeolocCountry, feed.GeolocRegion, feed.GeolocCity, feed.GeolocPostalCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return csvContent.ToString();
|
return csvContent.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Core.WebLogic\AS1024.GeoFeed.Core.WebLogic.csproj" />
|
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Core\AS1024.GeoFeed.Core.csproj" />
|
<ProjectReference Include="..\AS1024.GeoFeed.Core\AS1024.GeoFeed.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
|
|
||||||
# Install clang/zlib1g-dev dependencies for publishing to native
|
|
||||||
RUN apk add clang zlib-static zlib-dev musl-dev libc6-compat cmake autoconf make openssl-dev openssl-libs-static icu-static icu-dev
|
|
||||||
ARG BUILD_CONFIGURATION=Release
|
|
||||||
WORKDIR /src
|
|
||||||
COPY ["AS1024.GeoFeed.MinimalAPI/AS1024.GeoFeed.MinimalAPI.csproj", "AS1024.GeoFeed.MinimalAPI/"]
|
|
||||||
RUN dotnet restore "./AS1024.GeoFeed.MinimalAPI/./AS1024.GeoFeed.MinimalAPI.csproj"
|
|
||||||
COPY . .
|
|
||||||
WORKDIR "/src/AS1024.GeoFeed.MinimalAPI"
|
|
||||||
RUN dotnet build "./AS1024.GeoFeed.MinimalAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
ARG BUILD_CONFIGURATION=Release
|
|
||||||
RUN dotnet publish "./AS1024.GeoFeed.MinimalAPI.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:StaticOpenSslLinking=true /p:StaticExecutable=true /p:StaticallyLinked=true /p:StripSymbols=true /p:DebugType=None /p:DebugSymbols=false
|
|
||||||
|
|
||||||
FROM scratch AS final
|
|
||||||
WORKDIR /tmp
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 8080
|
|
||||||
COPY --from=publish /app/publish .
|
|
||||||
COPY --from=build /etc/ssl/certs/* /etc/ssl/certs/
|
|
||||||
ENTRYPOINT ["./AS1024.GeoFeed.MinimalAPI"]
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace AS1024.GeoFeed.MinimalAPI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override NetboxData? DeserializeJsonData(Stream stringResult)
|
protected override NetboxData? DeserializeJsonData(string stringResult)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize(stringResult, AppJsonSerializerContext.Default.NetboxData);
|
return JsonSerializer.Deserialize(stringResult, AppJsonSerializerContext.Default.NetboxData);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
using AS1024.GeoFeed.Core.CacheService;
|
using AS1024.GeoFeed.Core.CacheService;
|
||||||
using AS1024.GeoFeed.Core.GeoFeedPreloader;
|
using AS1024.GeoFeed.Core.GeoFeedProviders;
|
||||||
using AS1024.GeoFeed.Core.Interfaces;
|
using AS1024.GeoFeed.Core.Interfaces;
|
||||||
using AS1024.GeoFeed.Core.WebLogic;
|
using AS1024.GeoFeed.Core.Tools;
|
||||||
|
using AS1024.GeoFeed.Models;
|
||||||
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.MinimalAPI
|
namespace AS1024.GeoFeed.MinimalAPI
|
||||||
{
|
{
|
||||||
|
|
@ -13,29 +21,73 @@ namespace AS1024.GeoFeed.MinimalAPI
|
||||||
var builder = WebApplication.CreateSlimBuilder(args);
|
var builder = WebApplication.CreateSlimBuilder(args);
|
||||||
builder.Services.AddTransient<IGeoFeedProvider, NetboxAoTGeoFeedProvider>();
|
builder.Services.AddTransient<IGeoFeedProvider, NetboxAoTGeoFeedProvider>();
|
||||||
builder.Services.AddHostedService<GeoFeedCacheService>();
|
builder.Services.AddHostedService<GeoFeedCacheService>();
|
||||||
builder.Services.AddHostedService<PreLoadGeoFeed>();
|
|
||||||
builder.Services.AddTransient<IGeoFeedPersistentCacheProvider, GeoFeedLocalFileCache>();
|
builder.Services.AddTransient<IGeoFeedPersistentCacheProvider, GeoFeedLocalFileCache>();
|
||||||
builder.Services.AddScoped<GeoFeedReturn>();
|
|
||||||
builder.Services.AddMemoryCache();
|
builder.Services.AddMemoryCache();
|
||||||
builder.Services.AddLogging();
|
builder.Services.AddLogging();
|
||||||
builder.Services.AddHttpClient();
|
builder.Services.AddHttpClient();
|
||||||
builder.Services.ConfigureHttpJsonOptions(options =>
|
builder.Services.ConfigureHttpJsonOptions(options => {
|
||||||
{
|
|
||||||
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
|
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
|
||||||
});
|
});
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.Map("/geofeed.csv", async (GeoFeedReturn feedReturn) =>
|
app.Map("/geofeed.csv", async (IGeoFeedProvider provider,
|
||||||
{
|
ILogger<Program> logger,
|
||||||
return await feedReturn.GetGeoFeed();
|
IGeoFeedPersistentCacheProvider cacheProvider,
|
||||||
});
|
IMemoryCache memoryCache,
|
||||||
|
IWebHostEnvironment environment) => {
|
||||||
|
return await GeoFeedDataRunner(provider, logger, cacheProvider, memoryCache, environment);
|
||||||
|
});
|
||||||
|
|
||||||
app.Map("/geofeed", async (GeoFeedReturn feedReturn) =>
|
app.Map("/geofeed", async (IGeoFeedProvider provider,
|
||||||
{
|
ILogger<Program> logger,
|
||||||
return await feedReturn.GetGeoFeed();
|
IGeoFeedPersistentCacheProvider cacheProvider,
|
||||||
});
|
IMemoryCache memoryCache,
|
||||||
|
IWebHostEnvironment environment) => {
|
||||||
|
return await GeoFeedDataRunner(provider, logger, cacheProvider, memoryCache, environment);
|
||||||
|
});
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static async Task<IResult> GeoFeedDataRunner(IGeoFeedProvider provider,
|
||||||
|
ILogger<Program> logger,
|
||||||
|
IGeoFeedPersistentCacheProvider cacheProvider,
|
||||||
|
IMemoryCache memoryCache,
|
||||||
|
IWebHostEnvironment environment)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!memoryCache.TryGetValue("Geofeed", out List<IPGeoFeed>? feed))
|
||||||
|
{
|
||||||
|
feed = await provider.GetGeoFeedData();
|
||||||
|
if (environment.IsProduction())
|
||||||
|
{
|
||||||
|
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
|
.SetSlidingExpiration(TimeSpan.FromMinutes(15));
|
||||||
|
memoryCache.Set("Geofeed", feed, cacheEntryOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Results.File(Encoding.UTF8.GetBytes(feed.ToGeoFeedCsv()),
|
||||||
|
"text/csv",
|
||||||
|
"geofeed.csv");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning($"Temporary failure of retrieving GeoData from upstream. {ex}");
|
||||||
|
string geoFeedData = cacheProvider.GetGeoFeed();
|
||||||
|
|
||||||
|
return Results.File(Encoding.UTF8.GetBytes(geoFeedData),
|
||||||
|
"text/csv",
|
||||||
|
"geofeed.csv");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError($"Error: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Results.NoContent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
namespace AS1024.GeoFeed.Models
|
using System.Text.Json.Serialization;
|
||||||
|
namespace AS1024.GeoFeed.Models
|
||||||
{
|
{
|
||||||
public class NetboxData
|
public class NetboxData
|
||||||
{
|
{
|
||||||
public List<Result>? Results { get; set; }
|
public List<Result>? Results { get; set; }
|
||||||
public string? Next { get; set; }
|
public string? Next { get; set; }
|
||||||
public string? Previous { get; set; }
|
public string? Previous { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Result
|
public class Result
|
||||||
|
|
@ -40,8 +41,7 @@
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class represents the IP GeoFeed Entry
|
/// This class represents the IP GeoFeed Entry
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IPGeoFeed : CustomFields
|
public class IPGeoFeed : CustomFields {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the IP Prefix for the associated GeoFeed entry
|
/// Represents the IP Prefix for the associated GeoFeed entry
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AS1024.GeoFeed.Core", "AS10
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AS1024.GeoFeed.MinimalAPI", "AS1024.GeoFeed.MinimalAPI\AS1024.GeoFeed.MinimalAPI.csproj", "{36F2958C-8D0E-463B-9BF3-D6E55E6FC0B8}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AS1024.GeoFeed.MinimalAPI", "AS1024.GeoFeed.MinimalAPI\AS1024.GeoFeed.MinimalAPI.csproj", "{36F2958C-8D0E-463B-9BF3-D6E55E6FC0B8}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AS1024.GeoFeed.Core.SqliteGeoFeedCache", "AS1024.GeoFeed.Core.SqliteGeoFeedCache\AS1024.GeoFeed.Core.SqliteGeoFeedCache.csproj", "{3459BB31-FA7A-44D1-872D-C5338ACFBF80}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AS1024.GeoFeed.Core.SqliteGeoFeedCache", "AS1024.GeoFeed.Core.SqliteGeoFeedCache\AS1024.GeoFeed.Core.SqliteGeoFeedCache.csproj", "{3459BB31-FA7A-44D1-872D-C5338ACFBF80}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AS1024.GeoFeed.Core.WebLogic", "AS1024.GeoFeed.Core.WebLogic\AS1024.GeoFeed.Core.WebLogic.csproj", "{58BDCE89-FCC0-478F-BBDE-B89833712AAB}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
@ -44,10 +42,6 @@ Global
|
||||||
{3459BB31-FA7A-44D1-872D-C5338ACFBF80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3459BB31-FA7A-44D1-872D-C5338ACFBF80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3459BB31-FA7A-44D1-872D-C5338ACFBF80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3459BB31-FA7A-44D1-872D-C5338ACFBF80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3459BB31-FA7A-44D1-872D-C5338ACFBF80}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3459BB31-FA7A-44D1-872D-C5338ACFBF80}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{58BDCE89-FCC0-478F-BBDE-B89833712AAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{58BDCE89-FCC0-478F-BBDE-B89833712AAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{58BDCE89-FCC0-478F-BBDE-B89833712AAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{58BDCE89-FCC0-478F-BBDE-B89833712AAB}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Core.SqliteGeoFeedCache\AS1024.GeoFeed.Core.SqliteGeoFeedCache.csproj" />
|
<ProjectReference Include="..\AS1024.GeoFeed.Core.SqliteGeoFeedCache\AS1024.GeoFeed.Core.SqliteGeoFeedCache.csproj" />
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Core.WebLogic\AS1024.GeoFeed.Core.WebLogic.csproj" />
|
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Core\AS1024.GeoFeed.Core.csproj" />
|
<ProjectReference Include="..\AS1024.GeoFeed.Core\AS1024.GeoFeed.Core.csproj" />
|
||||||
<ProjectReference Include="..\AS1024.GeoFeed.Models\AS1024.GeoFeed.Models.csproj" />
|
<ProjectReference Include="..\AS1024.GeoFeed.Models\AS1024.GeoFeed.Models.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
using AS1024.GeoFeed.Core.WebLogic;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using AS1024.GeoFeed.Core.Interfaces;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using AS1024.GeoFeed.Models;
|
||||||
|
using System.Text;
|
||||||
|
using AS1024.GeoFeed.Core.Tools;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed.Controllers
|
namespace AS1024.GeoFeed.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -9,18 +13,75 @@ namespace AS1024.GeoFeed.Controllers
|
||||||
|
|
||||||
public class GeofeedController : ControllerBase
|
public class GeofeedController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly GeoFeedReturn feedReturn;
|
private readonly IGeoFeedProvider builder;
|
||||||
|
private readonly IMemoryCache memoryCache;
|
||||||
|
private readonly IWebHostEnvironment environment;
|
||||||
|
private readonly ILogger<GeofeedController> logger;
|
||||||
|
private readonly IGeoFeedPersistentCacheProvider geoFeedPersistentCache;
|
||||||
|
private const string GeoFeedCacheKey = "GeoFeedData";
|
||||||
|
|
||||||
public GeofeedController(GeoFeedReturn feedReturn)
|
public GeofeedController(IGeoFeedProvider builder,
|
||||||
{
|
IMemoryCache memoryCache,
|
||||||
this.feedReturn = feedReturn;
|
IWebHostEnvironment environment,
|
||||||
|
ILogger<GeofeedController> logger,
|
||||||
|
IGeoFeedPersistentCacheProvider geoFeedPersistentCache) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.geoFeedPersistentCache = geoFeedPersistentCache;
|
||||||
|
this.builder = builder;
|
||||||
|
this.memoryCache = memoryCache;
|
||||||
|
this.environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("")]
|
[Route("")]
|
||||||
public async Task<IResult> Get()
|
public async Task<IActionResult> Get()
|
||||||
{
|
{
|
||||||
return await feedReturn.GetGeoFeed();
|
try
|
||||||
|
{
|
||||||
|
if (!memoryCache.TryGetValue(GeoFeedCacheKey, out List<IPGeoFeed>? feed))
|
||||||
|
{
|
||||||
|
feed = await builder.GetGeoFeedData();
|
||||||
|
if (environment.IsProduction())
|
||||||
|
{
|
||||||
|
MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
|
.SetSlidingExpiration(TimeSpan.FromMinutes(15));
|
||||||
|
memoryCache.Set(GeoFeedCacheKey, feed, cacheEntryOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReturnFile(feed);
|
||||||
|
} catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning($"Temporary failure of retrieving GeoData from upstream. {ex}");
|
||||||
|
var cachedData = geoFeedPersistentCache.GetGeoFeed();
|
||||||
|
return ReturnFile(cachedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError($"Geofeed generation failed. Exception: {ex}");
|
||||||
|
return StatusCode(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
private IActionResult ReturnFile(List<IPGeoFeed>? feed)
|
||||||
|
{
|
||||||
|
string csvContent = feed.ToGeoFeedCsv(); // Assuming ToGeoFeedCsv() returns a string in CSV format.
|
||||||
|
return ReturnFile(csvContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
private IActionResult ReturnFile(string csvContent)
|
||||||
|
{
|
||||||
|
byte[] contentBytes = Encoding.UTF8.GetBytes(csvContent);
|
||||||
|
string contentType = "text/csv";
|
||||||
|
|
||||||
|
return new FileContentResult(contentBytes, contentType)
|
||||||
|
{
|
||||||
|
FileDownloadName = "geofeed.csv"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
using AS1024.GeoFeed.Core.CacheService;
|
using AS1024.GeoFeed.Core.Interfaces;
|
||||||
using AS1024.GeoFeed.Core.GeoFeedPreloader;
|
using AS1024.GeoFeed.Core.GeoFeedPreloader;
|
||||||
using AS1024.GeoFeed.Core.GeoFeedProviders;
|
using AS1024.GeoFeed.Core.GeoFeedProviders;
|
||||||
using AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache;
|
|
||||||
using AS1024.GeoFeed.Core.Interfaces;
|
|
||||||
using AS1024.GeoFeed.Core.WebLogic;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using AS1024.GeoFeed.Core.GeoFeedSqliteLocalCache;
|
||||||
|
using AS1024.GeoFeed.Core.CacheService;
|
||||||
|
|
||||||
namespace AS1024.GeoFeed
|
namespace AS1024.GeoFeed
|
||||||
{
|
{
|
||||||
|
|
@ -16,7 +15,6 @@ namespace AS1024.GeoFeed
|
||||||
|
|
||||||
builder.Services.AddHostedService<PreLoadGeoFeed>();
|
builder.Services.AddHostedService<PreLoadGeoFeed>();
|
||||||
builder.Services.AddTransient<IGeoFeedProvider, NetBoxGeoFeedProvider>();
|
builder.Services.AddTransient<IGeoFeedProvider, NetBoxGeoFeedProvider>();
|
||||||
builder.Services.AddScoped<GeoFeedReturn>();
|
|
||||||
builder.Services.AddDbContext<GeoFeedCacheDbContext>(
|
builder.Services.AddDbContext<GeoFeedCacheDbContext>(
|
||||||
options =>
|
options =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,8 @@ The application provides the following key endpoints:
|
||||||
|
|
||||||
## Security and Compliance
|
## Security and Compliance
|
||||||
|
|
||||||
This application is designed to always communicate over HTTPS with NetBox, ensuring that the data transfer is encrypted and secure.
|
This application is designed to always communicate over HTTPS with NetBox, ensuring that the data transfer is encrypted and secure.
|
||||||
|
|
||||||
## Extending Beyond NetBox
|
---
|
||||||
|
|
||||||
If your current IPAM solution is not netbox and wish to extend this web application to use the desired IPAM solution of choice, the interface `IGeoFeedProvider` is available for extensibility. To use your custom IPAM backend ensure that `NetboxAoTGeoFeedProvider` and `NetboxGeoFeedProvider` are not registered in the dependency injection container in the Web Apps. Once unregistered, register your custom IPAM communication backend provider to your needs and the web app should work in both AOT and MVC mode.
|
For more information about configuring and using this application, please refer to the official .NET documentation and the NetBox API guide.
|
||||||
|
|
||||||
Currently the Minimal API implementation of this web application only supports code that does not require reflection. This is a known limitation of native AOT deployments. If your code utilizes reflection or is not properly adapted for source generation, the minimal API version will **not work**.
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
{$GEOFEEDDOMAIN}:443 {
|
|
||||||
log {
|
|
||||||
level INFO
|
|
||||||
output file {$LOG_FILE} {
|
|
||||||
roll_size 10MB
|
|
||||||
roll_keep 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Use the ACME HTTP-01 challenge to get a cert for the configured domain.
|
|
||||||
tls {$EMAIL}
|
|
||||||
|
|
||||||
reverse_proxy geofeed:8080
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
version: '3.7'
|
|
||||||
services:
|
|
||||||
geofeed:
|
|
||||||
# use the image tag aot-minimal for the AOT version for fastest startup performance
|
|
||||||
image: git.startmywifi.com/as1024/geofeed:latest
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- './data:/data'
|
|
||||||
environment:
|
|
||||||
- ASPNETCORE_URLS=http://+:8080
|
|
||||||
- ConnectionString__LocalFeedCache=Data Source=/data/geofeed-cache.db
|
|
||||||
- APIKey=APIKeyHere
|
|
||||||
- NetBoxHost=netboxhosthere
|
|
||||||
caddy:
|
|
||||||
image: caddy:2
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 80:80
|
|
||||||
- 443:443
|
|
||||||
- 443:443/udp
|
|
||||||
volumes:
|
|
||||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
||||||
- ./caddy-config:/config
|
|
||||||
- ./caddy-data:/data
|
|
||||||
environment:
|
|
||||||
GEOFEEDDOMAIN: "https://geofeed.exampleas.net"
|
|
||||||
EMAIL: "noc@example.com" # The email address to use for ACME registration.
|
|
||||||
LOG_FILE: "/data/access.log"
|
|
||||||
Loading…
Reference in New Issue