feat: standalone Blazor WebAssembly admin + SEO enhancements
Architecture: - Admin UI: /admin (Standalone Blazor WebAssembly, 219 WASM files) - Portal: /portal (Razor Pages, Cookie/OAuth auth) - Homepage: / (Razor Pages, SSR) - API: /api (FastEndpoints + JWT) SEO: - Sitemap: Public content only (blog, FAQ, announcements, contact) - robots.txt: Exclude /admin and /portal, reference production domain - Naver verification: naverb1813cd79ddc2ded5c5291fca5cb46c2.html ready Technical: - TaxBaik.Web.Client: StaticWebAssetBasePath=admin - Server Program.cs: UseBlazorFrameworkFiles + MapFallback for SPA routing - base href="/admin/" for client-side navigation - blazor.webassembly.js (standalone, not web.js) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
namespace TaxBaik.Web.Client.Components.Admin.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Text.Json;
|
||||
|
||||
public interface IApiClient
|
||||
{
|
||||
Task<T?> GetAsync<T>(string endpoint);
|
||||
Task<T?> PostAsync<T>(string endpoint, object data);
|
||||
Task<T?> PutAsync<T>(string endpoint, object data);
|
||||
Task DeleteAsync(string endpoint);
|
||||
Task SetAuthToken(string? token);
|
||||
}
|
||||
|
||||
public class ApiClient : IApiClient
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly NavigationManager _navigationManager;
|
||||
private string? _authToken;
|
||||
|
||||
public ApiClient(HttpClient httpClient, NavigationManager navigationManager)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_navigationManager = navigationManager;
|
||||
}
|
||||
|
||||
public async Task SetAuthToken(string? token)
|
||||
{
|
||||
_authToken = token;
|
||||
if (token != null)
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new("Bearer", token);
|
||||
else
|
||||
_httpClient.DefaultRequestHeaders.Authorization = null;
|
||||
}
|
||||
|
||||
public async Task<T?> GetAsync<T>(string endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(BuildApiUri(endpoint));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return default;
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<T>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T?> PostAsync<T>(string endpoint, object data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(data);
|
||||
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PostAsync(BuildApiUri(endpoint), content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return default;
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<T>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T?> PutAsync<T>(string endpoint, object data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(data);
|
||||
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PutAsync(BuildApiUri(endpoint), content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return default;
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<T>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _httpClient.DeleteAsync(BuildApiUri(endpoint));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private Uri BuildApiUri(string endpoint)
|
||||
{
|
||||
var relative = $"api/{endpoint.TrimStart('/')}";
|
||||
return new Uri(new Uri(_navigationManager.BaseUri), relative);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user