Added API project.

This commit is contained in:
2025-09-02 23:44:31 -04:00
parent cb15940d34
commit 429252e61f
23 changed files with 474 additions and 49 deletions

19
JSMR.Api/JSMR.Api.csproj Normal file
View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>35cebc06-af6a-44cf-aa71-ecdaf1edc82b</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JSMR.Application\JSMR.Application.csproj" />
<ProjectReference Include="..\JSMR.Infrastructure\JSMR.Infrastructure.csproj" />
</ItemGroup>
</Project>

56
JSMR.Api/JSMR.Api.http Normal file
View File

@@ -0,0 +1,56 @@
@host = http://localhost:5226
@contentType = application/json
### Search tags by name
POST {{host}}/api/tags/search
Content-Type: {{contentType}}
{
"options": {
"criteria": {
"name": "Heart"
},
"pageNumber": 1,
"pageSize": 10
}
}
###
### Search voice works with circle filter
POST {{host}}/api/voiceworks/search
Content-Type: {{contentType}}
{
"options": {
"criteria": {
"circleStatus": "Favorited",
"releaseDateStart": "2023-01-01",
"releaseDateEnd": "2024-12-31",
"locale": "English",
"supportedLanguages": ["English"]
},
"pageNumber": 1,
"pageSize": 20,
"sortOptions": [
{ "field": "Downloads", "direction": "Descending" }
]
}
}
### Search voice works with keywords
POST {{host}}/api/voiceworks/search
Content-Type: {{contentType}}
{
"options": {
"criteria": {
"keywords": "tsundere maid"
},
"pageNumber": 1,
"pageSize": 20,
"sortOptions": [
{ "field": "Downloads", "direction": "Descending" }
]
}
}

103
JSMR.Api/Program.cs Normal file
View File

@@ -0,0 +1,103 @@
using JSMR.Application.Circles.Queries.Search;
using JSMR.Application.Creators.Queries.Search;
using JSMR.Application.DI;
using JSMR.Application.Tags.Queries.Search;
using JSMR.Application.VoiceWorks.Queries.Search;
using JSMR.Infrastructure.Data;
using JSMR.Infrastructure.DI;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services
.AddMemoryCache() // TODO
.AddApplication()
.AddInfrastructure();
// DbContext (MySQL here; swap to Npgsql when you migrate)
var cs = builder.Configuration.GetConnectionString("AppDb")
?? throw new InvalidOperationException("Missing ConnectionStrings:AppDb");
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseMySql(cs, ServerVersion.AutoDetect(cs))
.EnableSensitiveDataLogging(false));
builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
builder.Services.Configure<JsonOptions>(options =>
{
options.SerializerOptions.PropertyNameCaseInsensitive = true;
options.SerializerOptions.Converters.Add(
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); // or null for exact names
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// Health check
app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
// ---- Endpoints ----
// Circles Search
app.MapPost("/api/circles/search", async (
SearchCirclesRequest request,
SearchCirclesHandler handler,
CancellationToken cancallationToken) =>
{
var result = await handler.HandleAsync(request, cancallationToken);
return Results.Ok(result);
});
// Voice Works Search
app.MapPost("/api/voiceworks/search", async (
SearchVoiceWorksRequest request,
SearchVoiceWorksHandler handler,
CancellationToken cancallationToken) =>
{
var result = await handler.HandleAsync(request, cancallationToken);
return Results.Ok(result);
});
// Tags Search
app.MapPost("/api/tags/search", async (
SearchTagsRequest request,
SearchTagsHandler handler,
CancellationToken cancallationToken) =>
{
var result = await handler.HandleAsync(request, cancallationToken);
return Results.Ok(result);
});
// Creators Search
app.MapPost("/api/creators/search", async (
SearchCreatorsRequest request,
SearchCreatorsHandler handler,
CancellationToken cancallationToken) =>
{
var result = await handler.HandleAsync(request, cancallationToken);
return Results.Ok(result);
});
app.Run();

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5226",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7277;http://localhost:5226",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}