Files
Crussell/backend/handlers/user/gdpr_export.go
T
popertotsandSisyphus e4b9003439 refactor(handlers): migrate remaining backend handlers to clock.Now() and transaction patterns
Apply clock.Now() migration, transaction wrapping, and minor refactors across admin, scheduling, today, user, auth handler, notifications, webhooks, services, portfolio, ratelimit, testutils, and main.go.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-24 23:43:50 +01:00

103 lines
2.2 KiB
Go

package user
import (
"context"
"encoding/json"
"log"
"net/http"
"sync"
"time"
"crussell/db"
"crussell/clock"
"crussell/mw"
)
var (
gdprExportCache = make(map[string]*gdprCacheEntry)
gdprExportCacheMu sync.RWMutex
)
type gdprCacheEntry struct {
data json.RawMessage
expiresAt time.Time
generating bool
}
func init() {
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
func() {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic recovered in GDPR export cache cleanup ticker: %v", r)
}
}()
gdprExportCacheMu.Lock()
now := clock.Now()
for k, v := range gdprExportCache {
if now.After(v.expiresAt) {
delete(gdprExportCache, k)
}
}
gdprExportCacheMu.Unlock()
}()
}
}()
}
// GET /api/user/gdpr-export
func GetGDPRExportHandler(w http.ResponseWriter, r *http.Request) {
userID, ok := mw.GetUserID(r.Context())
if !ok {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
gdprExportCacheMu.Lock()
if entry, found := gdprExportCache[userID]; found && clock.Now().Before(entry.expiresAt) {
if entry.generating {
gdprExportCacheMu.Unlock()
w.Header().Set("X-Cache", "GENERATING")
w.Write([]byte(`{"status":"generating"}`))
return
}
gdprExportCacheMu.Unlock()
w.Header().Set("X-Cache", "HIT")
w.Write(entry.data)
return
}
gdprExportCache[userID] = &gdprCacheEntry{generating: true}
gdprExportCacheMu.Unlock()
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic recovered in GDPR export query: %v", r)
}
}()
var result json.RawMessage
err := db.Conn.QueryRow(context.Background(), `SELECT export_all_user_data($1)`, userID).Scan(&result)
if err != nil {
log.Printf("GDPR export failed for user %s: %v", userID, err)
gdprExportCacheMu.Lock()
delete(gdprExportCache, userID)
gdprExportCacheMu.Unlock()
return
}
gdprExportCacheMu.Lock()
gdprExportCache[userID] = &gdprCacheEntry{
data: result,
expiresAt: clock.Now().Add(12 * time.Hour),
}
gdprExportCacheMu.Unlock()
}()
w.Header().Set("X-Cache", "MISS")
w.Write([]byte(`{"status":"generating"}`))
}