backend: GDPR export + retention cleanups + worker metrics (BE-1/2/3)
BE-3 observability: expose the worker's Prometheus metrics on :6060/metrics (apns/fcm/asynq histograms + a new cache_ops_total counter were recorded all along but never scraped — which is why those dashboard panels read empty); add the worker containerPort, the vmagent worker scrape job, and two additive NetworkPolicies. Instrument cache Get/Set hit/miss. BE-2 retention: three periodic Asynq cleanup crons mirroring the reminder-log cleanup — notifications (90d), webhook dedup log (180d), audit_log (365d). BE-1 GDPR data export: POST /api/auth/export/ enqueues a low-priority Asynq job that gathers all of the user's data (owned residences + their tasks/contractors/ documents/share-codes, plus profile/notifications/prefs/push-tokens/subscription/ audit log), zips one JSON file per category, and emails it as an attachment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/treytartt/honeydue-api/internal/middleware"
|
||||
"github.com/treytartt/honeydue-api/internal/services"
|
||||
"github.com/treytartt/honeydue-api/internal/validator"
|
||||
"github.com/treytartt/honeydue-api/internal/worker"
|
||||
)
|
||||
|
||||
// AuthHandler handles user profile and account management endpoints.
|
||||
@@ -23,6 +24,7 @@ type AuthHandler struct {
|
||||
cache *services.CacheService
|
||||
storageService *services.StorageService
|
||||
auditService *services.AuditService
|
||||
enqueuer worker.Enqueuer
|
||||
}
|
||||
|
||||
// NewAuthHandler creates a new auth handler.
|
||||
@@ -44,6 +46,38 @@ func (h *AuthHandler) SetAuditService(auditService *services.AuditService) {
|
||||
h.auditService = auditService
|
||||
}
|
||||
|
||||
// SetEnqueuer sets the async task enqueuer (used by the GDPR data-export endpoint).
|
||||
func (h *AuthHandler) SetEnqueuer(enqueuer worker.Enqueuer) {
|
||||
h.enqueuer = enqueuer
|
||||
}
|
||||
|
||||
// ExportData handles POST /api/auth/export/ — queues a GDPR data-export job that
|
||||
// emails the user a zip of all their data. Async (202) because gathering,
|
||||
// zipping, and emailing can take seconds; doing it inline would block the request.
|
||||
func (h *AuthHandler) ExportData(c echo.Context) error {
|
||||
noStore(c)
|
||||
user, err := middleware.MustGetAuthUser(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if h.enqueuer == nil {
|
||||
return echo.NewHTTPError(http.StatusServiceUnavailable, "data export is temporarily unavailable")
|
||||
}
|
||||
if err := h.enqueuer.EnqueueDataExport(user.ID); err != nil {
|
||||
log.Error().Err(err).Uint("user_id", user.ID).Msg("Failed to enqueue data export")
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "failed to queue data export")
|
||||
}
|
||||
if h.auditService != nil {
|
||||
h.auditService.LogEvent(c, &user.ID, services.AuditEventDataExport, map[string]interface{}{
|
||||
"user_id": user.ID,
|
||||
"email": user.Email,
|
||||
})
|
||||
}
|
||||
return c.JSON(http.StatusAccepted, map[string]string{
|
||||
"message": "Your data export has been queued. You'll receive an email with your data shortly.",
|
||||
})
|
||||
}
|
||||
|
||||
// noStore marks a response as non-cacheable.
|
||||
func noStore(c echo.Context) {
|
||||
c.Response().Header().Set("Cache-Control", "no-store")
|
||||
|
||||
Reference in New Issue
Block a user