Add gallery admin and video media support.
This updates gallery handling to support video playback with generated poster thumbnails, adds authenticated admin upload/delete flows, and improves dev/runtime behavior including reliable thumbnail generation and media-safe response handling. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
99
app/internal/auth/auth.go
Normal file
99
app/internal/auth/auth.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const sessionCookie = "tk_admin_session"
|
||||
|
||||
// Config holds admin credentials from the environment.
|
||||
type Config struct {
|
||||
Username string
|
||||
Password string
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// Load reads admin auth settings. Admin is enabled when both user and password are set.
|
||||
func Load() Config {
|
||||
user := os.Getenv("ADMIN_USER")
|
||||
pass := os.Getenv("ADMIN_PASSWORD")
|
||||
return Config{
|
||||
Username: user,
|
||||
Password: pass,
|
||||
Enabled: user != "" && pass != "",
|
||||
}
|
||||
}
|
||||
|
||||
// Sessions tracks active login tokens in memory.
|
||||
type Sessions struct {
|
||||
mu sync.RWMutex
|
||||
tokens map[string]time.Time
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// NewSessions creates a session store.
|
||||
func NewSessions() *Sessions {
|
||||
return &Sessions{
|
||||
tokens: make(map[string]time.Time),
|
||||
ttl: 24 * time.Hour,
|
||||
}
|
||||
}
|
||||
|
||||
// Create issues a new session token.
|
||||
func (s *Sessions) Create() (string, error) {
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
token := base64.RawURLEncoding.EncodeToString(b)
|
||||
exp := time.Now().Add(s.ttl)
|
||||
s.mu.Lock()
|
||||
s.tokens[token] = exp
|
||||
s.mu.Unlock()
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Valid reports whether a session token is still active.
|
||||
func (s *Sessions) Valid(token string) bool {
|
||||
if token == "" {
|
||||
return false
|
||||
}
|
||||
s.mu.RLock()
|
||||
exp, ok := s.tokens[token]
|
||||
s.mu.RUnlock()
|
||||
if !ok || time.Now().After(exp) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Delete removes a session token.
|
||||
func (s *Sessions) Delete(token string) {
|
||||
s.mu.Lock()
|
||||
delete(s.tokens, token)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// CookieName returns the session cookie name.
|
||||
func CookieName() string {
|
||||
return sessionCookie
|
||||
}
|
||||
|
||||
// CheckCredentials compares username and password to config using constant-time compare.
|
||||
func CheckCredentials(cfg Config, username, password string) bool {
|
||||
if !cfg.Enabled {
|
||||
return false
|
||||
}
|
||||
uOK := subtle.ConstantTimeCompare([]byte(username), []byte(cfg.Username)) == 1
|
||||
pOK := subtle.ConstantTimeCompare([]byte(password), []byte(cfg.Password)) == 1
|
||||
return uOK && pOK
|
||||
}
|
||||
|
||||
// ErrDisabled is returned when admin auth is not configured.
|
||||
var ErrDisabled = errors.New("admin auth is not configured")
|
||||
Reference in New Issue
Block a user