session.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package storage // import "miniflux.app/v2/internal/storage"
  4. import (
  5. "crypto/rand"
  6. "database/sql"
  7. "fmt"
  8. "time"
  9. "miniflux.app/v2/internal/model"
  10. )
  11. // CreateAppSessionWithUserPrefs creates a new application session with the given user preferences.
  12. func (s *Storage) CreateAppSessionWithUserPrefs(userID int64) (*model.Session, error) {
  13. user, err := s.UserByID(userID)
  14. if err != nil {
  15. return nil, err
  16. }
  17. session := model.Session{
  18. ID: rand.Text(),
  19. Data: &model.SessionData{
  20. CSRF: rand.Text(),
  21. Theme: user.Theme,
  22. Language: user.Language,
  23. },
  24. }
  25. return s.createAppSession(&session)
  26. }
  27. // CreateAppSession creates a new application session.
  28. func (s *Storage) CreateAppSession() (*model.Session, error) {
  29. session := model.Session{
  30. ID: rand.Text(),
  31. Data: &model.SessionData{
  32. CSRF: rand.Text(),
  33. },
  34. }
  35. return s.createAppSession(&session)
  36. }
  37. func (s *Storage) createAppSession(session *model.Session) (*model.Session, error) {
  38. query := `INSERT INTO sessions (id, data) VALUES ($1, $2)`
  39. _, err := s.db.Exec(query, session.ID, session.Data)
  40. if err != nil {
  41. return nil, fmt.Errorf(`store: unable to create app session: %v`, err)
  42. }
  43. return session, nil
  44. }
  45. // SetAppSessionTextField sets a text field in the session data.
  46. func (s *Storage) SetAppSessionTextField(sessionID, field string, value any) error {
  47. query := `
  48. UPDATE
  49. sessions
  50. SET
  51. data = jsonb_set(data, ARRAY[$2::text], to_jsonb($1::text), true)
  52. WHERE
  53. id=$3
  54. `
  55. _, err := s.db.Exec(query, value, field, sessionID)
  56. if err != nil {
  57. return fmt.Errorf(`store: unable to update session text field %q: %v`, field, err)
  58. }
  59. return nil
  60. }
  61. // SetAppSessionJSONField sets a JSON field in the session data.
  62. func (s *Storage) SetAppSessionJSONField(sessionID, field string, value any) error {
  63. query := `
  64. UPDATE
  65. sessions
  66. SET
  67. data = jsonb_set(data, ARRAY[$2::text], $1, true)
  68. WHERE
  69. id=$3
  70. `
  71. _, err := s.db.Exec(query, value, field, sessionID)
  72. if err != nil {
  73. return fmt.Errorf(`store: unable to update session JSON field %q: %v`, field, err)
  74. }
  75. return nil
  76. }
  77. // AppSession returns the given session.
  78. func (s *Storage) AppSession(id string) (*model.Session, error) {
  79. var session model.Session
  80. query := "SELECT id, data FROM sessions WHERE id=$1"
  81. err := s.db.QueryRow(query, id).Scan(
  82. &session.ID,
  83. &session.Data,
  84. )
  85. switch {
  86. case err == sql.ErrNoRows:
  87. return nil, fmt.Errorf(`store: session not found: %s`, id)
  88. case err != nil:
  89. return nil, fmt.Errorf(`store: unable to fetch session: %v`, err)
  90. default:
  91. return &session, nil
  92. }
  93. }
  94. // FlushAllSessions removes all sessions from the database.
  95. func (s *Storage) FlushAllSessions() (err error) {
  96. _, err = s.db.Exec(`DELETE FROM user_sessions`)
  97. if err != nil {
  98. return err
  99. }
  100. _, err = s.db.Exec(`DELETE FROM sessions`)
  101. if err != nil {
  102. return err
  103. }
  104. return nil
  105. }
  106. // CleanOldSessions removes sessions older than specified interval (24h minimum).
  107. func (s *Storage) CleanOldSessions(interval time.Duration) int64 {
  108. query := `
  109. DELETE FROM
  110. sessions
  111. WHERE
  112. created_at < now() - $1::interval
  113. `
  114. days := max(int(interval/(24*time.Hour)), 1)
  115. result, err := s.db.Exec(query, fmt.Sprintf("%d days", days))
  116. if err != nil {
  117. return 0
  118. }
  119. n, _ := result.RowsAffected()
  120. return n
  121. }