icon.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Copyright 2017 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package storage
  5. import (
  6. "database/sql"
  7. "fmt"
  8. "strings"
  9. "time"
  10. "github.com/miniflux/miniflux2/helper"
  11. "github.com/miniflux/miniflux2/model"
  12. )
  13. // HasIcon checks if the given feed has an icon.
  14. func (s *Storage) HasIcon(feedID int64) bool {
  15. var result int
  16. query := `SELECT count(*) as c FROM feed_icons WHERE feed_id=$1`
  17. s.db.QueryRow(query, feedID).Scan(&result)
  18. return result == 1
  19. }
  20. // IconByID returns an icon by the ID.
  21. func (s *Storage) IconByID(iconID int64) (*model.Icon, error) {
  22. defer helper.ExecutionTime(time.Now(), "[Storage:IconByID]")
  23. var icon model.Icon
  24. query := `SELECT id, hash, mime_type, content FROM icons WHERE id=$1`
  25. err := s.db.QueryRow(query, iconID).Scan(&icon.ID, &icon.Hash, &icon.MimeType, &icon.Content)
  26. if err == sql.ErrNoRows {
  27. return nil, nil
  28. } else if err != nil {
  29. return nil, fmt.Errorf("Unable to fetch icon by hash: %v", err)
  30. }
  31. return &icon, nil
  32. }
  33. // IconByHash returns an icon by the hash (checksum).
  34. func (s *Storage) IconByHash(icon *model.Icon) error {
  35. defer helper.ExecutionTime(time.Now(), "[Storage:IconByHash]")
  36. err := s.db.QueryRow(`SELECT id FROM icons WHERE hash=$1`, icon.Hash).Scan(&icon.ID)
  37. if err == sql.ErrNoRows {
  38. return nil
  39. } else if err != nil {
  40. return fmt.Errorf("Unable to fetch icon by hash: %v", err)
  41. }
  42. return nil
  43. }
  44. // CreateIcon creates a new icon.
  45. func (s *Storage) CreateIcon(icon *model.Icon) error {
  46. defer helper.ExecutionTime(time.Now(), "[Storage:CreateIcon]")
  47. query := `
  48. INSERT INTO icons
  49. (hash, mime_type, content)
  50. VALUES
  51. ($1, $2, $3)
  52. RETURNING id
  53. `
  54. err := s.db.QueryRow(
  55. query,
  56. icon.Hash,
  57. normalizeMimeType(icon.MimeType),
  58. icon.Content,
  59. ).Scan(&icon.ID)
  60. if err != nil {
  61. return fmt.Errorf("Unable to create icon: %v", err)
  62. }
  63. return nil
  64. }
  65. // CreateFeedIcon creates an icon and associate the icon to the given feed.
  66. func (s *Storage) CreateFeedIcon(feed *model.Feed, icon *model.Icon) error {
  67. defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CreateFeedIcon] feedID=%d", feed.ID))
  68. err := s.IconByHash(icon)
  69. if err != nil {
  70. return err
  71. }
  72. if icon.ID == 0 {
  73. err := s.CreateIcon(icon)
  74. if err != nil {
  75. return err
  76. }
  77. }
  78. _, err = s.db.Exec(`INSERT INTO feed_icons (feed_id, icon_id) VALUES ($1, $2)`, feed.ID, icon.ID)
  79. if err != nil {
  80. return fmt.Errorf("unable to create feed icon: %v", err)
  81. }
  82. return nil
  83. }
  84. // Icons returns all icons tht belongs to a user.
  85. func (s *Storage) Icons(userID int64) (model.Icons, error) {
  86. defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:Icons] userID=%d", userID))
  87. query := `
  88. SELECT
  89. icons.id, icons.hash, icons.mime_type, icons.content
  90. FROM icons
  91. LEFT JOIN feed_icons ON feed_icons.icon_id=icons.id
  92. LEFT JOIN feeds ON feeds.id=feed_icons.feed_id
  93. WHERE feeds.user_id=$1
  94. `
  95. rows, err := s.db.Query(query, userID)
  96. if err != nil {
  97. return nil, fmt.Errorf("unable to fetch icons: %v", err)
  98. }
  99. defer rows.Close()
  100. var icons model.Icons
  101. for rows.Next() {
  102. var icon model.Icon
  103. err := rows.Scan(&icon.ID, &icon.Hash, &icon.MimeType, &icon.Content)
  104. if err != nil {
  105. return nil, fmt.Errorf("unable to fetch icons row: %v", err)
  106. }
  107. icons = append(icons, &icon)
  108. }
  109. return icons, nil
  110. }
  111. func normalizeMimeType(mimeType string) string {
  112. mimeType = strings.ToLower(mimeType)
  113. switch mimeType {
  114. case "image/png", "image/jpeg", "image/jpg", "image/webp", "image/svg+xml", "image/x-icon", "image/gif":
  115. return mimeType
  116. default:
  117. return "image/x-icon"
  118. }
  119. }