extension.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package feed
  2. import (
  3. "fmt"
  4. "html"
  5. "html/template"
  6. "io"
  7. "log/slog"
  8. "net/http"
  9. "net/url"
  10. )
  11. type ExtensionType int
  12. const (
  13. ExtensionContentHTML ExtensionType = iota
  14. ExtensionContentUnknown = iota
  15. )
  16. var ExtensionStringToType = map[string]ExtensionType{
  17. "html": ExtensionContentHTML,
  18. }
  19. const (
  20. ExtensionHeaderTitle = "Widget-Title"
  21. ExtensionHeaderContentType = "Widget-Content-Type"
  22. )
  23. type ExtensionRequestOptions struct {
  24. URL string `yaml:"url"`
  25. Parameters map[string]string `yaml:"parameters"`
  26. AllowHtml bool `yaml:"allow-potentially-dangerous-html"`
  27. }
  28. type Extension struct {
  29. Title string
  30. Content template.HTML
  31. }
  32. func convertExtensionContent(options ExtensionRequestOptions, content []byte, contentType ExtensionType) template.HTML {
  33. switch contentType {
  34. case ExtensionContentHTML:
  35. if options.AllowHtml {
  36. return template.HTML(content)
  37. }
  38. fallthrough
  39. default:
  40. return template.HTML(html.EscapeString(string(content)))
  41. }
  42. }
  43. func FetchExtension(options ExtensionRequestOptions) (Extension, error) {
  44. request, _ := http.NewRequest("GET", options.URL, nil)
  45. query := url.Values{}
  46. for key, value := range options.Parameters {
  47. query.Set(key, value)
  48. }
  49. request.URL.RawQuery = query.Encode()
  50. response, err := http.DefaultClient.Do(request)
  51. if err != nil {
  52. slog.Error("failed fetching extension", "error", err, "url", options.URL)
  53. return Extension{}, fmt.Errorf("%w: request failed: %w", ErrNoContent, err)
  54. }
  55. defer response.Body.Close()
  56. body, err := io.ReadAll(response.Body)
  57. if err != nil {
  58. slog.Error("failed reading response body of extension", "error", err, "url", options.URL)
  59. return Extension{}, fmt.Errorf("%w: could not read body: %w", ErrNoContent, err)
  60. }
  61. extension := Extension{}
  62. if response.Header.Get(ExtensionHeaderTitle) == "" {
  63. extension.Title = "Extension"
  64. } else {
  65. extension.Title = response.Header.Get(ExtensionHeaderTitle)
  66. }
  67. contentType, ok := ExtensionStringToType[response.Header.Get(ExtensionHeaderContentType)]
  68. if !ok {
  69. contentType = ExtensionContentUnknown
  70. }
  71. extension.Content = convertExtensionContent(options, body, contentType)
  72. return extension, nil
  73. }