lookbook/internal/data/session/queries.go
soup 523831cb8d
Add password change functionality
- Add POST /api/auth/password API endpoint
  - Requires authentication and current password verification
  - Invalidates all other sessions after password change
  - Keeps current session active

- Add window.changePassword() console function
  - Matches existing login flow pattern
  - Usage: changePassword("current", "new")

- Add 'lookbook set-password' CLI command
  - Interactive password reset (no current password required)
  - Useful for recovery scenarios
  - Invalidates all sessions

- Add session.QDeleteAllExcept() and session.QDeleteAll()
  - Support for invalidating sessions after password change
2026-01-17 22:28:13 -05:00

87 lines
2 KiB
Go

package session
import (
"context"
"database/sql"
"time"
)
type Row struct {
ID int64
SessionID string
CreatedAt time.Time
ExpiresAt time.Time
}
// QCreate creates a new session.
func QCreate(ctx context.Context, db *sql.DB, sessionID string, expiresAt time.Time) (Row, error) {
query := `
INSERT INTO session (session_id, expires_at)
VALUES ($1, $2)
RETURNING id, session_id, created_at, expires_at
`
var row Row
err := db.QueryRowContext(ctx, query, sessionID, expiresAt).Scan(
&row.ID,
&row.SessionID,
&row.CreatedAt,
&row.ExpiresAt,
)
return row, err
}
// QFindBySessionID finds a session by its session ID.
// Returns (nil, nil) if the session does not exist.
func QFindBySessionID(ctx context.Context, db *sql.DB, sessionID string) (*Row, error) {
query := `
SELECT id, session_id, created_at, expires_at
FROM session
WHERE session_id = $1
LIMIT 1
`
var row Row
err := db.QueryRowContext(ctx, query, sessionID).Scan(
&row.ID,
&row.SessionID,
&row.CreatedAt,
&row.ExpiresAt,
)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
return &row, nil
}
// QDelete deletes a session by its session ID.
func QDelete(ctx context.Context, db *sql.DB, sessionID string) error {
query := `DELETE FROM session WHERE session_id = $1`
_, err := db.ExecContext(ctx, query, sessionID)
return err
}
// QDeleteExpired deletes all expired sessions.
func QDeleteExpired(ctx context.Context, db *sql.DB) error {
query := `DELETE FROM session WHERE expires_at < NOW()`
_, err := db.ExecContext(ctx, query)
return err
}
// QDeleteAllExcept deletes all sessions except the one with the given session ID.
func QDeleteAllExcept(ctx context.Context, db *sql.DB, exceptSessionID string) error {
query := `DELETE FROM session WHERE session_id != $1`
_, err := db.ExecContext(ctx, query, exceptSessionID)
return err
}
// QDeleteAll deletes all sessions.
func QDeleteAll(ctx context.Context, db *sql.DB) error {
query := `DELETE FROM session`
_, err := db.ExecContext(ctx, query)
return err
}