mirror of
https://github.com/miniflux/v2.git
synced 2025-06-27 16:36:00 +00:00
Go 1.24 provides the helpful rand.Text() function, returning a base32-encoded string containing at least 128 bits of randomness. We should make use of it everywhere it makes sense to do so, if only to not having to think about much entropy do we need for each cases, and just trust the go crypto team. Also, rand.Read() can't fail, so no need to check its return value: https://pkg.go.dev/crypto/rand#Read This behaviour is consistent with go's standard library itself.
140 lines
3.2 KiB
Go
140 lines
3.2 KiB
Go
// 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"
|
|
|
|
"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
|
|
}
|
|
|
|
// UpdateAppSessionField updates only one session field.
|
|
func (s *Storage) UpdateAppSessionField(sessionID, field string, value any) error {
|
|
query := `
|
|
UPDATE
|
|
sessions
|
|
SET
|
|
data = jsonb_set(data, '{%s}', to_jsonb($1::text), true)
|
|
WHERE
|
|
id=$2
|
|
`
|
|
_, err := s.db.Exec(fmt.Sprintf(query, field), value, sessionID)
|
|
if err != nil {
|
|
return fmt.Errorf(`store: unable to update session field: %v`, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Storage) UpdateAppSessionObjectField(sessionID, field string, value interface{}) error {
|
|
query := `
|
|
UPDATE
|
|
sessions
|
|
SET
|
|
data = jsonb_set(data, '{%s}', $1, true)
|
|
WHERE
|
|
id=$2
|
|
`
|
|
_, err := s.db.Exec(fmt.Sprintf(query, field), value, sessionID)
|
|
if err != nil {
|
|
return fmt.Errorf(`store: unable to update session field: %v`, 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 days.
|
|
func (s *Storage) CleanOldSessions(days int) int64 {
|
|
query := `
|
|
DELETE FROM
|
|
sessions
|
|
WHERE
|
|
created_at < now() - $1::interval
|
|
`
|
|
result, err := s.db.Exec(query, fmt.Sprintf("%d days", days))
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
n, _ := result.RowsAffected()
|
|
return n
|
|
}
|