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:
2026-06-02 23:01:02 +12:00
parent 509e7ccb43
commit 45b31be9a7
22 changed files with 1002 additions and 217 deletions

99
app/internal/auth/auth.go Normal file
View 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")