fix(worker): Base-URL ohne Schema laesst die Ticket-Anbindung scheitern
Ein blanker Hostname ist eine relative URI; HttpClient lehnt sie mit InvalidOperationException ab. Die lief an dem zu engen catch-Filter vorbei, flog roh aus dem Hub und wurde von TryInvokeAsync zu null verschluckt - die UI meldete "Worker not reachable", obwohl der Worker lief und die Ursache eine Eingabe war. - TicketSystemConfig ergaenzt fehlendes http:// - BandelTicketClient uebersetzt jede Transport-Exception in eine lesbare TicketApiException - TestTicketConnection faengt alles und antwortet mit dem Grund - GetTicketSettings liefert die effektive URL, damit die Ergaenzung sichtbar ist
This commit is contained in:
@@ -1274,8 +1274,10 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// Liefert die EFFEKTIVE Base-URL (inkl. ergänztem Schema), nicht den rohen Feldwert: sonst
|
||||
// sieht der Nutzer im Tab weiter "host.local", während der Client "http://host.local" ruft.
|
||||
public TicketSettingsDto GetTicketSettings()
|
||||
=> new(_cfg.TicketApiBaseUrl, _ticketConfig.Token is not null);
|
||||
=> new(_ticketConfig.BaseUrl, _ticketConfig.Token is not null);
|
||||
|
||||
public void SetTicketApiBaseUrl(string? baseUrl)
|
||||
{
|
||||
@@ -1301,7 +1303,10 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
var identity = await client.GetIdentityAsync(Context.ConnectionAborted);
|
||||
return new TicketConnectionDto(true, identity.UserName, identity.Scopes, null);
|
||||
}
|
||||
catch (TicketApiException ex)
|
||||
// "Verbindung testen" ist die eine Stelle, an der ein Fehler eine ANTWORT ist und keine
|
||||
// Ausnahme: wirft der Hub, schluckt WorkerClient.TryInvokeAsync das Ergebnis zu null und
|
||||
// die UI zeigt "Worker not reachable" statt des Grundes. Deshalb hier alles einfangen.
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new TicketConnectionDto(false, null, Array.Empty<string>(), ex.Message);
|
||||
}
|
||||
@@ -1311,8 +1316,15 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
{
|
||||
var client = _ticketClients.Create();
|
||||
if (client is null) return new List<TicketProjectDto>();
|
||||
var projects = await client.GetProjectsAsync(Context.ConnectionAborted);
|
||||
return projects.Select(p => new TicketProjectDto(p.Id, p.Title, p.DepartmentName)).ToList();
|
||||
try
|
||||
{
|
||||
var projects = await client.GetProjectsAsync(Context.ConnectionAborted);
|
||||
return projects.Select(p => new TicketProjectDto(p.Id, p.Title, p.DepartmentName)).ToList();
|
||||
}
|
||||
catch (TicketApiException ex)
|
||||
{
|
||||
throw new HubException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<TicketImportResultDto> ImportTickets(string listId)
|
||||
|
||||
@@ -64,7 +64,12 @@ public sealed class BandelTicketClient
|
||||
{
|
||||
response = await _http.SendAsync(request, ct);
|
||||
}
|
||||
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException && !ct.IsCancellationRequested)
|
||||
// Bewusst JEDE Exception, nicht nur HttpRequestException/TaskCanceledException: eine
|
||||
// fehlerhafte Base-URL wirft InvalidOperationException oder UriFormatException, und die
|
||||
// flogen vorher roh aus dem Hub. Der Aufrufer bekam dadurch statt eines Grundes nur ein
|
||||
// verschlucktes null — die UI meldete "Worker not reachable", obwohl der Worker lief.
|
||||
// Eine echte Abbruchanforderung wird davon nicht angefasst.
|
||||
catch (Exception ex) when (!ct.IsCancellationRequested)
|
||||
{
|
||||
throw new TicketApiException($"Ticketsystem nicht erreichbar ({_baseUrl}): {ex.Message}");
|
||||
}
|
||||
|
||||
@@ -25,9 +25,25 @@ public sealed class TicketSystemConfig
|
||||
_pat = pat;
|
||||
}
|
||||
|
||||
public string? BaseUrl => string.IsNullOrWhiteSpace(_worker.TicketApiBaseUrl)
|
||||
? null
|
||||
: _worker.TicketApiBaseUrl.Trim().TrimEnd('/');
|
||||
/// <summary>
|
||||
/// Die Base-URL, wie HttpClient sie braucht: absolut, ohne Schrägstrich am Ende.
|
||||
///
|
||||
/// Ein Feld namens "API base URL" lädt dazu ein, einen blanken Hostnamen einzutippen.
|
||||
/// Ohne Schema ist das aber eine RELATIVE URI, und HttpClient lehnt sie mit einer
|
||||
/// InvalidOperationException ab, lange bevor ein Paket fließt. Deshalb wird hier — und nur
|
||||
/// hier, weil das der einzige Leser ist — `http://` ergänzt. `http` und nicht `https`, weil
|
||||
/// die API im Intranet ohne TLS läuft; wer TLS will, tippt das Schema hin und es bleibt stehen.
|
||||
/// </summary>
|
||||
public string? BaseUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
var raw = _worker.TicketApiBaseUrl?.Trim();
|
||||
if (string.IsNullOrEmpty(raw)) return null;
|
||||
if (!raw.Contains("://", StringComparison.Ordinal)) raw = "http://" + raw;
|
||||
return raw.TrimEnd('/');
|
||||
}
|
||||
}
|
||||
|
||||
public string? Token => _pat.Store.Read();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user