Fixed scanning issue. Updated worker.
Some checks failed
ci / build-test (push) Has been cancelled
ci / publish-image (push) Has been cancelled

This commit is contained in:
2026-02-14 22:47:19 -05:00
parent 340c62d18b
commit a85989a337
14 changed files with 286 additions and 36 deletions

View File

@@ -48,6 +48,7 @@ public static class InfrastructureServiceCollectionExtensions
services.AddKeyedScoped<IVoiceWorkUpdater, VoiceWorkUpdater>(Locale.Japanese);
services.AddKeyedScoped<IVoiceWorkUpdater, EnglishVoiceWorkUpdater>(Locale.English);
services.AddScoped<IVoiceWorkSearchUpdater, VoiceWorkSearchUpdater>();
//services.AddKeyedScoped<ISupportedLanguage, JapaneseLanguage>(Locale.Japanese);
//services.AddKeyedScoped<ISupportedLanguage, EnglishLanguage>(Locale.English);
@@ -64,27 +65,34 @@ public static class InfrastructureServiceCollectionExtensions
services.AddSingleton<ICache, MemoryCacheAdapter>();
services.AddSingleton<ISpamCircleCache, SpamCircleCache>();
services.AddHttpClient<IHttpService, HttpService>(client =>
{
client.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
});
services.AddScoped<IHttpService, HttpService>();
services.AddScoped<IHtmlLoader, HtmlLoader>();
services.AddSingleton<ILanguageIdentifier, LanguageIdentifier>();
services.AddSingleton<IClock, Clock>();
services.AddSingleton<ITimeProvider, TokyoTimeProvider>();
services.AddHttpClient<IDLSiteClient, DLSiteClient>(httpClient =>
services.AddHttpServices();
return services;
}
private static IServiceCollection AddHttpServices(this IServiceCollection services)
{
//services.AddHttpClient<IHttpService, HttpService>(client =>
//{
// client.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
//});
//services.AddScoped<IHttpService, HttpService>();
services.AddScoped<IHtmlLoader, HtmlLoader>();
// ONE registration for IHttpService as a typed client:
services.AddHttpClient<IHttpService, HttpService>((sp, http) =>
{
httpClient.BaseAddress = new Uri("https://www.dlsite.com/");
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0 (+contact@example.com)");
httpClient.Timeout = TimeSpan.FromSeconds(15);
http.BaseAddress = new Uri("https://www.dlsite.com/");
http.DefaultRequestHeaders.UserAgent.ParseAdd("JSMR/1.0");
http.Timeout = TimeSpan.FromSeconds(15);
})
.AddResilienceHandler("dlsite", builder =>
{
.AddResilienceHandler("dlsite", builder => {
builder.AddRetry(new HttpRetryStrategyOptions
{
MaxRetryAttempts = 3,
@@ -93,21 +101,13 @@ public static class InfrastructureServiceCollectionExtensions
BackoffType = DelayBackoffType.Exponential,
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.Handle<HttpRequestException>()
.HandleResult(msg =>
msg.StatusCode == (HttpStatusCode)429 ||
(int)msg.StatusCode >= 500)
.HandleResult(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 429)
});
// (Optional) add a circuit breaker:
// builder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
// {
// FailureRatio = 0.2,
// SamplingDuration = TimeSpan.FromSeconds(30),
// MinimumThroughput = 20,
// BreakDuration = TimeSpan.FromSeconds(15)
// });
});
// Register DLSiteClient as a normal scoped service
services.AddScoped<IDLSiteClient, DLSiteClient>();
return services;
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace JSMR.Infrastructure.Data;
public sealed class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
// adjust base path if needed (points to the worker for secrets/env)
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.AddJsonFile("appsettings.Development.json", optional: true)
.AddUserSecrets(typeof(AppDbContextFactory).Assembly, optional: true)
.AddEnvironmentVariables()
.Build();
var conn = config.GetConnectionString("AppDb")
?? throw new InvalidOperationException("Missing ConnectionStrings:AppDb");
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseMySql(conn, ServerVersion.AutoDetect(conn))
.EnableSensitiveDataLogging(false)
.Options;
return new AppDbContext(options);
}
}

View File

@@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging;
namespace JSMR.Infrastructure.Integrations.DLSite;
public class DLSiteClient(IHttpService http, ILogger logger) : ApiClient(http, logger), IDLSiteClient
public class DLSiteClient(IHttpService http, ILogger<DLSiteClient> logger) : ApiClient(http, logger), IDLSiteClient
{
public async Task<VoiceWorkDetailCollection> GetVoiceWorkDetailsAsync(string[] productIds, CancellationToken cancellationToken = default)
{

View File

@@ -6,8 +6,31 @@ namespace JSMR.Infrastructure.Integrations.DLSite.Serialization;
public sealed class DictionaryConverter<TKey, TValue> : JsonConverter<Dictionary<TKey, TValue>> where TKey : notnull
{
public override Dictionary<TKey, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> JsonSerializer.Deserialize<Dictionary<TKey, TValue>>(ref reader, options);
{
if (reader.TokenType == JsonTokenType.Null)
return null;
if (reader.TokenType == JsonTokenType.StartArray)
{
if (!reader.Read())
throw new JsonException("Unexpected end while reading array.");
if (reader.TokenType != JsonTokenType.EndArray)
throw new JsonException("Non-empty JSON array cannot be converted to Dictionary.");
return [];
}
if (reader.TokenType == JsonTokenType.StartObject)
{
return JsonSerializer.Deserialize<Dictionary<TKey, TValue>>(ref reader, options) ?? [];
}
throw new JsonException($"Unexpected token {reader.TokenType} when reading Dictionary.");
}
public override void Write(Utf8JsonWriter writer, Dictionary<TKey, TValue> value, JsonSerializerOptions options)
=> JsonSerializer.Serialize(writer, value, options);
{
JsonSerializer.Serialize(writer, (IDictionary<TKey, TValue>)value, options);
}
}

View File

@@ -19,6 +19,9 @@
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.10.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />