File: session.go

package info (click to toggle)
miniflux 2.2.16-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,188 kB
  • sloc: xml: 4,853; javascript: 1,158; sh: 257; makefile: 161
file content (145 lines) | stat: -rw-r--r-- 3,401 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package storage // import "miniflux.app/v2/internal/storage"

import (
	"crypto/rand"
	"database/sql"
	"fmt"
	"time"

	"miniflux.app/v2/internal/model"
)

// CreateAppSessionWithUserPrefs creates a new application session with the given user preferences.
func (s *Storage) CreateAppSessionWithUserPrefs(userID int64) (*model.Session, error) {
	user, err := s.UserByID(userID)
	if err != nil {
		return nil, err
	}

	session := model.Session{
		ID: rand.Text(),
		Data: &model.SessionData{
			CSRF:     rand.Text(),
			Theme:    user.Theme,
			Language: user.Language,
		},
	}

	return s.createAppSession(&session)
}

// CreateAppSession creates a new application session.
func (s *Storage) CreateAppSession() (*model.Session, error) {
	session := model.Session{
		ID: rand.Text(),
		Data: &model.SessionData{
			CSRF: rand.Text(),
		},
	}

	return s.createAppSession(&session)
}

func (s *Storage) createAppSession(session *model.Session) (*model.Session, error) {
	query := `INSERT INTO sessions (id, data) VALUES ($1, $2)`
	_, err := s.db.Exec(query, session.ID, session.Data)
	if err != nil {
		return nil, fmt.Errorf(`store: unable to create app session: %v`, err)
	}

	return session, nil
}

// SetAppSessionTextField sets a text field in the session data.
func (s *Storage) SetAppSessionTextField(sessionID, field string, value any) error {
	query := `
		UPDATE
			sessions
		SET
			data = jsonb_set(data, ARRAY[$2::text], to_jsonb($1::text), true)
		WHERE
			id=$3
	`
	_, err := s.db.Exec(query, value, field, sessionID)
	if err != nil {
		return fmt.Errorf(`store: unable to update session text field %q: %v`, field, err)
	}

	return nil
}

// SetAppSessionJSONField sets a JSON field in the session data.
func (s *Storage) SetAppSessionJSONField(sessionID, field string, value any) error {
	query := `
		UPDATE
			sessions
		SET
			data = jsonb_set(data, ARRAY[$2::text], $1, true)
		WHERE
			id=$3
	`
	_, err := s.db.Exec(query, value, field, sessionID)
	if err != nil {
		return fmt.Errorf(`store: unable to update session JSON field %q: %v`, field, err)
	}

	return nil
}

// AppSession returns the given session.
func (s *Storage) AppSession(id string) (*model.Session, error) {
	var session model.Session

	query := "SELECT id, data FROM sessions WHERE id=$1"
	err := s.db.QueryRow(query, id).Scan(
		&session.ID,
		&session.Data,
	)

	switch {
	case err == sql.ErrNoRows:
		return nil, fmt.Errorf(`store: session not found: %s`, id)
	case err != nil:
		return nil, fmt.Errorf(`store: unable to fetch session: %v`, err)
	default:
		return &session, nil
	}
}

// FlushAllSessions removes all sessions from the database.
func (s *Storage) FlushAllSessions() (err error) {
	_, err = s.db.Exec(`DELETE FROM user_sessions`)
	if err != nil {
		return err
	}

	_, err = s.db.Exec(`DELETE FROM sessions`)
	if err != nil {
		return err
	}

	return nil
}

// CleanOldSessions removes sessions older than specified interval (24h minimum).
func (s *Storage) CleanOldSessions(interval time.Duration) int64 {
	query := `
		DELETE FROM
			sessions
		WHERE
			created_at < now() - $1::interval
	`

	days := max(int(interval/(24*time.Hour)), 1)

	result, err := s.db.Exec(query, fmt.Sprintf("%d days", days))
	if err != nil {
		return 0
	}

	n, _ := result.RowsAffected()
	return n
}