fix(developers): distinguish API errors from taken usernames in availability check
All checks were successful
Build and Deploy / deploy (push) Successful in 40s
All checks were successful
Build and Deploy / deploy (push) Successful in 40s
CheckUsernameAsync returned false (taken) on any API failure, making every username appear taken when SilverDESK was unreachable. Now returns nullable bool so errors show a warning instead of blocking submission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -107,6 +107,10 @@
|
||||
{
|
||||
<span class="username-status username-taken">✗ Username is already taken</span>
|
||||
}
|
||||
else if (_usernameCheckState == UsernameCheckState.Error)
|
||||
{
|
||||
<span class="username-status username-error">⚠ Could not check availability — you can still submit</span>
|
||||
}
|
||||
<ValidationMessage For="() => _application.DesiredUsername" />
|
||||
</div>
|
||||
|
||||
@@ -209,7 +213,7 @@
|
||||
{
|
||||
<div class="dev-error">@_errorMessage</div>
|
||||
}
|
||||
<button type="submit" class="dev-btn dev-btn-primary" disabled="@_submitting">
|
||||
<button type="submit" class="dev-btn dev-btn-primary" disabled="@(_submitting || _usernameCheckState == UsernameCheckState.Taken)">
|
||||
@if (_submitting)
|
||||
{
|
||||
<span class="btn-spinner"></span>
|
||||
@@ -240,7 +244,7 @@
|
||||
|
||||
private readonly string[] _availablePlatforms = { "Windows", "macOS", "Linux", "Android", "iOS", "Other" };
|
||||
|
||||
private enum UsernameCheckState { None, Checking, Available, Taken }
|
||||
private enum UsernameCheckState { None, Checking, Available, Taken, Error }
|
||||
|
||||
private void SelectRole(ApplicationRole role)
|
||||
{
|
||||
@@ -279,7 +283,12 @@
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
_usernameCheckState = available ? UsernameCheckState.Available : UsernameCheckState.Taken;
|
||||
_usernameCheckState = available switch
|
||||
{
|
||||
true => UsernameCheckState.Available,
|
||||
false => UsernameCheckState.Taken,
|
||||
null => UsernameCheckState.Error
|
||||
};
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ public static class DeveloperEndpoints
|
||||
group.MapGet("/check-username/{username}", async (string username, DeveloperApplicationService service) =>
|
||||
{
|
||||
var available = await service.CheckUsernameAsync(username);
|
||||
return Results.Ok(new { available });
|
||||
if (available is null)
|
||||
return Results.Problem("Unable to verify username availability", statusCode: 503);
|
||||
return Results.Ok(new { available = available.Value });
|
||||
});
|
||||
|
||||
group.MapPost("/apply", async (DeveloperApplication application, DeveloperApplicationService service) =>
|
||||
|
||||
@@ -17,21 +17,30 @@ public class DeveloperApplicationService
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<bool> CheckUsernameAsync(string username)
|
||||
/// <summary>
|
||||
/// Checks username availability. Returns: true = available, false = taken, null = error/unknown.
|
||||
/// </summary>
|
||||
public async Task<bool?> CheckUsernameAsync(string username)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync($"/api/auth/check-username/{Uri.EscapeDataString(username)}");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return false;
|
||||
{
|
||||
_logger.LogWarning("Username check returned {StatusCode} for {Username}", response.StatusCode, username);
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
return result.TryGetProperty("available", out var available) && available.GetBoolean();
|
||||
if (result.TryGetProperty("available", out var available))
|
||||
return available.GetBoolean();
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking username availability for {Username}", username);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +186,29 @@
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
/* Username status */
|
||||
.username-status {
|
||||
display: block;
|
||||
font-size: 0.82rem;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.username-checking {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.username-available {
|
||||
color: #34d399;
|
||||
}
|
||||
|
||||
.username-taken {
|
||||
color: #f87171;
|
||||
}
|
||||
|
||||
.username-error {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
/* Validation messages */
|
||||
.validation-message {
|
||||
color: #f87171;
|
||||
|
||||
Reference in New Issue
Block a user