mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 00:26:07 +02:00
docs: Update documentation for 145 files
Generated by AurionDocs
Job ID: 934f8c39-8082-4942-8d17-72ed8f5f8d50
Source commit: 40aea9a
This commit is contained in:
@@ -8,86 +8,13 @@
|
||||
```mermaid
|
||||
%%{init: {'theme':'base','themeVariables':{'background':'#faf7ef','primaryColor':'#f0e2c2','primaryTextColor':'#1f2840','primaryBorderColor':'#8a7548','secondaryColor':'#d9efec','secondaryBorderColor':'#1d8a80','secondaryTextColor':'#1f2840','tertiaryColor':'#f2ebd8','tertiaryBorderColor':'#8a7548','tertiaryTextColor':'#1f2840','lineColor':'#1d8a80','titleColor':'#1f2840','fontSize':'14px','edgeLabelBackground':'#faf7ef','clusterBkg':'#f2ebd8','clusterBorder':'#8a7548','actorBkg':'#f0e2c2','actorBorder':'#8a7548','actorTextColor':'#1f2840','actorLineColor':'#8a7548','signalColor':'#1d8a80','signalTextColor':'#1f2840','activationBkgColor':'#d9efec','activationBorderColor':'#1d8a80','noteBkgColor':'#f2ebd8','noteBorderColor':'#8a7548','noteTextColor':'#1f2840','labelBoxBkgColor':'#f0e2c2','labelBoxBorderColor':'#8a7548','labelTextColor':'#1f2840','transitionColor':'#1d8a80','transitionLabelColor':'#1f2840','stateLabelColor':'#1f2840','altBackground':'#f2ebd8'}}}%%
|
||||
flowchart TB
|
||||
Start["Start"]
|
||||
Extract["ExtractUrls(content) -> urls"]
|
||||
CheckUrls["urls.Count == 0?"]
|
||||
ReturnNullNoUrls["Return null (no URLs found)"]
|
||||
InitEmbeds["Create empty List#60;EmbedDto#62; embeds"]
|
||||
ForEach["For each url in urls"]
|
||||
CallFetch["Call FetchEmbedForUrlAsync(url)"]
|
||||
ReturnNullFromFetch["Returned null -> continue"]
|
||||
AddEmbed["Add EmbedDto to embeds"]
|
||||
CatchLog["Catch Exception -> LogDebug and continue"]
|
||||
AfterLoop["All URLs processed"]
|
||||
ReturnDecision["embeds.Count > 0?"]
|
||||
ReturnEmbeds["Return embeds"]
|
||||
ReturnNullAll["Return null (no successful embeds)"]
|
||||
LinkEmbedService["TryGetEmbedsAsync: ExtractUrls content; if no URLs -> return null. For each URL: call FetchEmbedForUrlAsync -> validate absolute URI, allow http or https, skip private hosts; create CancellationTokenSource using HubConstants, send GET with HttpClient 'OgFetch' and HttpCompletionOption.ResponseHeadersRead; if non-success status -> skip; ensure Content-Type starts with text/html; read limited HTML; parse OG tags; determine title with og:title fallback to <title>; if no title -> skip; else build EmbedDto and add to results. Catch exceptions and LogDebug. Return embeds list or null"]
|
||||
HubConstants["HubConstants: EmbedFetchTimeoutSeconds, EmbedMaxHtmlBytes, EmbedMaxDescription"]
|
||||
EmbedDto["EmbedDto: represents successful OG embed data"]
|
||||
|
||||
subgraph FetchEmbedForUrlAsync
|
||||
F1["Try Uri.TryCreate(url, Absolute)"]
|
||||
F1_no["Return null (invalid uri)"]
|
||||
F2["Check scheme is http or https"]
|
||||
F2_no["Return null (unsupported scheme)"]
|
||||
F3["IsPrivateHost(uri)?"]
|
||||
F3_no["Return null (private host)"]
|
||||
F4["Create CTS with HubConstants.EmbedFetchTimeoutSeconds"]
|
||||
F5["Create HTTP client 'OgFetch'"]
|
||||
F6["Send GET request, get response"]
|
||||
F7["response.IsSuccessStatusCode?"]
|
||||
F7_no["Return null (unsuccessful status)"]
|
||||
F8["Content-Type starts with #quot;text/html#quot;?"]
|
||||
F8_no["Return null (non-html content)"]
|
||||
F9["Read limited HTML (HubConstants.EmbedMaxHtmlBytes)"]
|
||||
F9_empty["Return null (empty or whitespace html)"]
|
||||
F10["Parse OG tags, get title or fall back to #60;title#62;"]
|
||||
F10_no["Return null (no title)"]
|
||||
F11["Build EmbedDto and return"]
|
||||
end
|
||||
|
||||
Start --> Extract
|
||||
Extract --> CheckUrls
|
||||
CheckUrls -->|"yes"| ReturnNullNoUrls
|
||||
CheckUrls -->|"no"| InitEmbeds
|
||||
InitEmbeds --> ForEach
|
||||
ForEach --> CallFetch
|
||||
CallFetch -->|"throws"| CatchLog
|
||||
CallFetch -->|"null"| ReturnNullFromFetch
|
||||
CallFetch -->|"EmbedDto"| AddEmbed
|
||||
ReturnNullFromFetch --> ForEach
|
||||
AddEmbed --> ForEach
|
||||
CatchLog --> ForEach
|
||||
ForEach -->|"done"| AfterLoop
|
||||
AfterLoop --> ReturnDecision
|
||||
ReturnDecision -->|"yes"| ReturnEmbeds
|
||||
ReturnDecision -->|"no"| ReturnNullAll
|
||||
|
||||
CallFetch --> F1
|
||||
F1 -->|"no"| F1_no
|
||||
F1 -->|"yes"| F2
|
||||
F2 -->|"no"| F2_no
|
||||
F2 -->|"yes"| F3
|
||||
F3 -->|"true"| F3_no
|
||||
F3 -->|"false"| F4
|
||||
F4 --> F5
|
||||
F5 --> F6
|
||||
F6 --> F7
|
||||
F7 -->|"no"| F7_no
|
||||
F7 -->|"yes"| F8
|
||||
F8 -->|"no"| F8_no
|
||||
F8 -->|"yes"| F9
|
||||
F9 -->|"empty"| F9_empty
|
||||
F9 -->|"has html"| F10
|
||||
F10 -->|"no"| F10_no
|
||||
F10 -->|"yes"| F11
|
||||
|
||||
F1_no --> ReturnNullFromFetch
|
||||
F2_no --> ReturnNullFromFetch
|
||||
F3_no --> ReturnNullFromFetch
|
||||
F7_no --> ReturnNullFromFetch
|
||||
F8_no --> ReturnNullFromFetch
|
||||
F9_empty --> ReturnNullFromFetch
|
||||
F10_no --> ReturnNullFromFetch
|
||||
F11 --> AddEmbed
|
||||
LinkEmbedService -->|"reads timeouts and limits"| HubConstants
|
||||
LinkEmbedService -->|"creates and adds successful EmbedDto"| EmbedDto
|
||||
LinkEmbedService -->|"foreach URL (loop)"| LinkEmbedService
|
||||
```
|
||||
|
||||
```csharp
|
||||
@@ -95,30 +22,13 @@ public partial class LinkEmbedService
|
||||
```
|
||||
|
||||
|
||||
Scans a piece of message text for URLs and attempts to produce lightweight link preview data (EmbedDto) by fetching and parsing Open Graph and common HTML metadata. Use TryGetEmbedsAsync when you need server-side link previews for chat messages and want a defensive, timeout- and size-limited fetch that never throws (it logs failures and returns null when no usable embeds are found).
|
||||
Detects and fetches Open Graph-style embed metadata for any URLs found in a piece of message `content`. Use `LinkEmbedService` (via its `TryGetEmbedsAsync` method) when you want a best-effort, non-throwing attempt to produce [`EmbedDto`](../../EchoHub.Core/DTOs/ChatDtos.cs.md) objects for links inside user messages — for example, to show link previews — and you want network, size and privacy protections applied automatically.
|
||||
|
||||
## Remarks
|
||||
LinkEmbedService centralizes the logic for discovering URLs in a message and converting remote HTML metadata into EmbedDto instances suitable for display. It is intentionally defensive: only absolute http/https URLs are considered, private hosts are skipped, fetches are limited by a cancellation timeout and a maximum HTML byte count (HubConstants), and only text/html responses are parsed. Errors during individual fetches are caught and logged at debug level so the caller observes either a list of successful embeds or null (no useful embeds).
|
||||
|
||||
## Example
|
||||
```csharp
|
||||
// Given an instance of LinkEmbedService (typically from DI):
|
||||
var embeds = await linkEmbedService.TryGetEmbedsAsync(messageContent);
|
||||
if (embeds is null)
|
||||
{
|
||||
// No embeds found or all fetch attempts failed.
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Found {embeds.Count} embeds");
|
||||
foreach (var embed in embeds)
|
||||
{
|
||||
// render embed in UI or pass to presentation layer
|
||||
}
|
||||
}
|
||||
```
|
||||
`LinkEmbedService` centralizes link-preview logic so callers do not have to implement URL extraction, host-safety checks, HTTP fetching, HTML-size limits, or Open Graph parsing themselves. The public `TryGetEmbedsAsync` method returns `null` when no useful embed data is available (either because no URLs were found or all fetch attempts failed) and never throws; individual fetch failures are caught and logged at debug level. Internally it calls the private `FetchEmbedForUrlAsync` for each URL which enforces absolute `http`/`https` URIs, rejects private hosts via `IsPrivateHost`, uses an `IHttpClientFactory`-created client named `"OgFetch"`, applies a `CancellationTokenSource` timeout (`HubConstants.EmbedFetchTimeoutSeconds`), requires a `text/html` response, bounds the HTML read size (`HubConstants.EmbedMaxHtmlBytes`), extracts Open Graph tags (falling back to the `<title>` tag), decodes HTML entities with `WebUtility.HtmlDecode`, and truncates long descriptions to `HubConstants.EmbedMaxDescriptionLength`.
|
||||
|
||||
## Notes
|
||||
- TryGetEmbedsAsync returns null when no URLs are present or when all fetches fail; it does not return an empty list in those cases—check for null before iterating.
|
||||
- The service expects an IHttpClientFactory and creates a client with the name "OgFetch"; ensure your HttpClient configuration (handlers, DNS/timeout policies) is appropriate for remote HTML fetches.
|
||||
- HTML metadata extraction is heuristic: it uses Open Graph tags, falls back to a <title> regex, reads only the first N bytes of HTML, and truncates long descriptions per HubConstants. Consumers should treat returned fields as untrusted display content and apply any necessary sanitization in the UI layer.
|
||||
- The service expects an `IHttpClientFactory` client named `"OgFetch"` to be configured; network policy (proxies, handlers) should be applied on that named client rather than relying on this class to set HTTP options.
|
||||
- Fetching is constrained by time and size: a cancellation timeout (`HubConstants.EmbedFetchTimeoutSeconds`) and a maximum number of HTML bytes (`HubConstants.EmbedMaxHtmlBytes`) are enforced; pages that exceed these limits may yield no embed.
|
||||
- Only absolute `http`/`https` URLs are considered and private/internal hosts are explicitly ignored by `IsPrivateHost`; the method will return `null` instead of an [`EmbedDto`](../../EchoHub.Core/DTOs/ChatDtos.cs.md) for such URLs.
|
||||
- Failures during individual URL fetches are swallowed (logged at debug) so `TryGetEmbedsAsync` remains non-throwing for callers — check logs when embeds are unexpectedly missing.
|
||||
Reference in New Issue
Block a user