Address PR review: graceful shutdown, Role/NewUser, drop SQLite.
This commit is contained in:
+6
-122
@@ -2,22 +2,12 @@ package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/alexedwards/scs/postgresstore"
|
||||
"github.com/alexedwards/scs/v2"
|
||||
)
|
||||
|
||||
const sessionsSchemaSQLite = `
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
token TEXT PRIMARY KEY,
|
||||
data BLOB NOT NULL,
|
||||
expiry REAL NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS sessions_expiry_idx ON sessions(expiry);
|
||||
`
|
||||
|
||||
const sessionsSchemaPostgres = `
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
token TEXT PRIMARY KEY,
|
||||
@@ -27,12 +17,9 @@ CREATE TABLE IF NOT EXISTS sessions (
|
||||
CREATE INDEX IF NOT EXISTS sessions_expiry_idx ON sessions (expiry);
|
||||
`
|
||||
|
||||
func applySessionsSchema(db *sql.DB, dialect string) error {
|
||||
schema := sessionsSchemaSQLite
|
||||
if dialect == dialectPostgres {
|
||||
schema = sessionsSchemaPostgres
|
||||
}
|
||||
return applySchema(db, schema)
|
||||
// applySessionsSchema creates the scs sessions table if missing.
|
||||
func applySessionsSchema(db *sql.DB) error {
|
||||
return applySchema(db, sessionsSchemaPostgres)
|
||||
}
|
||||
|
||||
type sessionStopper interface {
|
||||
@@ -45,110 +32,7 @@ func (s *Store) SessionStore() scs.Store {
|
||||
}
|
||||
|
||||
func (s *Store) initSessionStore(cleanupInterval time.Duration) {
|
||||
switch s.dialect {
|
||||
case dialectPostgres:
|
||||
ps := postgresstore.NewWithCleanupInterval(s.db, cleanupInterval)
|
||||
s.sessionStore = ps
|
||||
s.sessionStopper = ps
|
||||
default:
|
||||
ss := newSQLiteSessionStore(s.db, cleanupInterval)
|
||||
s.sessionStore = ss
|
||||
s.sessionStopper = ss
|
||||
}
|
||||
ps := postgresstore.NewWithCleanupInterval(s.db, cleanupInterval)
|
||||
s.sessionStore = ps
|
||||
s.sessionStopper = ps
|
||||
}
|
||||
|
||||
// sqliteSessionStore is a modernc-safe scs.Store (uses ? placeholders).
|
||||
type sqliteSessionStore struct {
|
||||
db *sql.DB
|
||||
stopCleanup chan bool
|
||||
}
|
||||
|
||||
func newSQLiteSessionStore(db *sql.DB, cleanupInterval time.Duration) *sqliteSessionStore {
|
||||
s := &sqliteSessionStore{db: db}
|
||||
if cleanupInterval > 0 {
|
||||
s.stopCleanup = make(chan bool)
|
||||
go s.startCleanup(cleanupInterval)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) Find(token string) ([]byte, bool, error) {
|
||||
var b []byte
|
||||
err := s.db.QueryRow(
|
||||
`SELECT data FROM sessions WHERE token = ? AND julianday('now') < expiry`,
|
||||
token,
|
||||
).Scan(&b)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return b, true, nil
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) Commit(token string, b []byte, expiry time.Time) error {
|
||||
_, err := s.db.Exec(
|
||||
`REPLACE INTO sessions (token, data, expiry) VALUES (?, ?, julianday(?))`,
|
||||
token,
|
||||
b,
|
||||
expiry.UTC().Format("2006-01-02T15:04:05.999"),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) Delete(token string) error {
|
||||
_, err := s.db.Exec(`DELETE FROM sessions WHERE token = ?`, token)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) All() (map[string][]byte, error) {
|
||||
rows, err := s.db.Query(`SELECT token, data FROM sessions WHERE julianday('now') < expiry`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := make(map[string][]byte)
|
||||
for rows.Next() {
|
||||
var token string
|
||||
var data []byte
|
||||
if err := rows.Scan(&token, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[token] = data
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) startCleanup(interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := s.deleteExpired(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
case <-s.stopCleanup:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) StopCleanup() {
|
||||
if s.stopCleanup != nil {
|
||||
s.stopCleanup <- true
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sqliteSessionStore) deleteExpired() error {
|
||||
_, err := s.db.Exec(`DELETE FROM sessions WHERE expiry < julianday('now')`)
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure interface compliance.
|
||||
var (
|
||||
_ scs.Store = (*sqliteSessionStore)(nil)
|
||||
_ scs.IterableStore = (*sqliteSessionStore)(nil)
|
||||
_ sessionStopper = (*sqliteSessionStore)(nil)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user