262 lines
8.8 KiB
C#
262 lines
8.8 KiB
C#
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add services
|
|
builder.Services.AddHttpClient();
|
|
builder.Services.AddCors(options =>
|
|
{
|
|
options.AddDefaultPolicy(policy =>
|
|
{
|
|
policy.AllowAnyOrigin()
|
|
.AllowAnyMethod()
|
|
.AllowAnyHeader();
|
|
});
|
|
});
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseCors();
|
|
app.UseDefaultFiles();
|
|
app.UseStaticFiles();
|
|
|
|
// Update this to point to your local API or production API
|
|
const string API_BASE_URL = "https://citelynq.com/api";
|
|
|
|
// Proxy endpoint for full-text search
|
|
app.MapGet("/api/search", async (HttpContext context, IHttpClientFactory httpClientFactory, ILogger<Program> logger) =>
|
|
{
|
|
try
|
|
{
|
|
var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault();
|
|
|
|
if (string.IsNullOrEmpty(apiKey))
|
|
{
|
|
return Results.BadRequest(new { error = "API key is required" });
|
|
}
|
|
|
|
// Get query parameters
|
|
var query = context.Request.Query["q"].FirstOrDefault();
|
|
var source = context.Request.Query["source"].FirstOrDefault();
|
|
var limit = context.Request.Query["limit"].FirstOrDefault() ?? "20";
|
|
|
|
if (string.IsNullOrEmpty(query))
|
|
{
|
|
return Results.BadRequest(new { error = "Search query (q) is required" });
|
|
}
|
|
|
|
// Build URL for CiteLynq API
|
|
var httpClient = httpClientFactory.CreateClient();
|
|
var url = $"{API_BASE_URL}/search?q={Uri.EscapeDataString(query)}&limit={limit}";
|
|
|
|
if (!string.IsNullOrEmpty(source))
|
|
{
|
|
url += $"&source={Uri.EscapeDataString(source)}";
|
|
}
|
|
|
|
logger.LogInformation("Calling CiteLynq API: {Url}", url);
|
|
|
|
var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
|
|
httpRequest.Headers.Add("X-API-Key", apiKey);
|
|
|
|
var response = await httpClient.SendAsync(httpRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
logger.LogInformation("CiteLynq API Response Status: {StatusCode}", response.StatusCode);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
logger.LogError("CiteLynq API Error: {Response}", responseContent);
|
|
try
|
|
{
|
|
var errorObj = JsonSerializer.Deserialize<object>(responseContent);
|
|
return Results.Json(errorObj, statusCode: (int)response.StatusCode);
|
|
}
|
|
catch
|
|
{
|
|
return Results.Json(new { error = responseContent }, statusCode: (int)response.StatusCode);
|
|
}
|
|
}
|
|
|
|
return Results.Content(responseContent, "application/json");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error in search endpoint");
|
|
return Results.Json(new { error = ex.Message, details = ex.ToString() }, statusCode: 500);
|
|
}
|
|
});
|
|
|
|
// Proxy endpoint for semantic search
|
|
app.MapGet("/api/search/semantic", async (HttpContext context, IHttpClientFactory httpClientFactory, ILogger<Program> logger) =>
|
|
{
|
|
try
|
|
{
|
|
var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault();
|
|
|
|
if (string.IsNullOrEmpty(apiKey))
|
|
{
|
|
return Results.BadRequest(new { error = "API key is required" });
|
|
}
|
|
|
|
// Get query parameters
|
|
var query = context.Request.Query["q"].FirstOrDefault();
|
|
var source = context.Request.Query["source"].FirstOrDefault();
|
|
var limit = context.Request.Query["limit"].FirstOrDefault() ?? "20";
|
|
|
|
if (string.IsNullOrEmpty(query))
|
|
{
|
|
return Results.BadRequest(new { error = "Search query (q) is required" });
|
|
}
|
|
|
|
// Build URL for CiteLynq API
|
|
var httpClient = httpClientFactory.CreateClient();
|
|
var url = $"{API_BASE_URL}/search/semantic?q={Uri.EscapeDataString(query)}&limit={limit}";
|
|
|
|
if (!string.IsNullOrEmpty(source))
|
|
{
|
|
url += $"&source={Uri.EscapeDataString(source)}";
|
|
}
|
|
|
|
logger.LogInformation("Calling CiteLynq API: {Url}", url);
|
|
|
|
var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
|
|
httpRequest.Headers.Add("X-API-Key", apiKey);
|
|
|
|
var response = await httpClient.SendAsync(httpRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
logger.LogInformation("CiteLynq API Response Status: {StatusCode}", response.StatusCode);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
logger.LogError("CiteLynq API Error: {Response}", responseContent);
|
|
try
|
|
{
|
|
var errorObj = JsonSerializer.Deserialize<object>(responseContent);
|
|
return Results.Json(errorObj, statusCode: (int)response.StatusCode);
|
|
}
|
|
catch
|
|
{
|
|
return Results.Json(new { error = responseContent }, statusCode: (int)response.StatusCode);
|
|
}
|
|
}
|
|
|
|
return Results.Content(responseContent, "application/json");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error in semantic search endpoint");
|
|
return Results.Json(new { error = ex.Message, details = ex.ToString() }, statusCode: 500);
|
|
}
|
|
});
|
|
|
|
// Proxy endpoint for date-based search
|
|
app.MapGet("/api/search/bydate", async (HttpContext context, IHttpClientFactory httpClientFactory, ILogger<Program> logger) =>
|
|
{
|
|
try
|
|
{
|
|
var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault();
|
|
|
|
if (string.IsNullOrEmpty(apiKey))
|
|
{
|
|
return Results.BadRequest(new { error = "API key is required" });
|
|
}
|
|
|
|
// Get query parameters
|
|
var query = context.Request.Query["q"].FirstOrDefault();
|
|
var month = context.Request.Query["month"].FirstOrDefault();
|
|
var day = context.Request.Query["day"].FirstOrDefault();
|
|
var source = context.Request.Query["source"].FirstOrDefault();
|
|
var limit = context.Request.Query["limit"].FirstOrDefault() ?? "20";
|
|
|
|
if (string.IsNullOrEmpty(query))
|
|
{
|
|
return Results.BadRequest(new { error = "Search query (q) is required" });
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(month) || string.IsNullOrEmpty(day))
|
|
{
|
|
return Results.BadRequest(new { error = "Month and day parameters are required for date search" });
|
|
}
|
|
|
|
// Build URL for CiteLynq API
|
|
var httpClient = httpClientFactory.CreateClient();
|
|
var url = $"{API_BASE_URL}/search/bydate?q={Uri.EscapeDataString(query)}&month={month}&day={day}&limit={limit}";
|
|
|
|
if (!string.IsNullOrEmpty(source))
|
|
{
|
|
url += $"&source={Uri.EscapeDataString(source)}";
|
|
}
|
|
|
|
logger.LogInformation("Calling CiteLynq API: {Url}", url);
|
|
|
|
var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
|
|
httpRequest.Headers.Add("X-API-Key", apiKey);
|
|
|
|
var response = await httpClient.SendAsync(httpRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
logger.LogInformation("CiteLynq API Response Status: {StatusCode}", response.StatusCode);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
logger.LogError("CiteLynq API Error: {Response}", responseContent);
|
|
try
|
|
{
|
|
var errorObj = JsonSerializer.Deserialize<object>(responseContent);
|
|
return Results.Json(errorObj, statusCode: (int)response.StatusCode);
|
|
}
|
|
catch
|
|
{
|
|
return Results.Json(new { error = responseContent }, statusCode: (int)response.StatusCode);
|
|
}
|
|
}
|
|
|
|
return Results.Content(responseContent, "application/json");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error in date search endpoint");
|
|
return Results.Json(new { error = ex.Message, details = ex.ToString() }, statusCode: 500);
|
|
}
|
|
});
|
|
|
|
// Proxy endpoint to get article details
|
|
app.MapGet("/api/articles/{id}", async (string id, HttpContext context, IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
try
|
|
{
|
|
var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault();
|
|
|
|
if (string.IsNullOrEmpty(apiKey))
|
|
{
|
|
return Results.BadRequest(new { error = "API key is required" });
|
|
}
|
|
|
|
var httpClient = httpClientFactory.CreateClient();
|
|
var httpRequest = new HttpRequestMessage(HttpMethod.Get, $"{API_BASE_URL}/articles/{id}");
|
|
httpRequest.Headers.Add("X-API-Key", apiKey);
|
|
|
|
var response = await httpClient.SendAsync(httpRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
return Results.Json(
|
|
JsonSerializer.Deserialize<object>(responseContent),
|
|
statusCode: (int)response.StatusCode
|
|
);
|
|
}
|
|
|
|
return Results.Content(responseContent, "application/json");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.Problem(ex.Message);
|
|
}
|
|
});
|
|
|
|
app.Run();
|