Files
david 788b6cf430 Add data export functionality with gzip compression
- Add FindByUserID method to HabitEntryRepository (JOIN with habits)
- Implement ExportUserDataHandler to export all user data
- Add GET /api/v1/export endpoint with gzip compression
- Apply strict rate limiting (1 export per hour per user)
- Export includes all habits (active + archived) and entries
- Add i18n translations for export errors (en/es)
- Update all test mocks to implement new repository method
- Export format: JSON with gzip (~10x compression ratio)
2025-12-03 22:32:22 +01:00

66 lines
1.7 KiB
Go

package http
import (
"compress/gzip"
"encoding/json"
"net/http"
"apocapoc-api/internal/application/queries"
"apocapoc-api/internal/i18n"
)
type ExportHandlers struct {
exportHandler *queries.ExportUserDataHandler
translator *i18n.Translator
}
func NewExportHandlers(
exportHandler *queries.ExportUserDataHandler,
translator *i18n.Translator,
) *ExportHandlers {
return &ExportHandlers{
exportHandler: exportHandler,
translator: translator,
}
}
// ExportData godoc
// @Summary Export user data
// @Description Export all user habits and entries in JSON format with gzip compression. Limited to 1 export per hour.
// @Tags export
// @Security BearerAuth
// @Produce json
// @Success 200 {object} queries.ExportUserDataResult "Compressed JSON export"
// @Failure 401 {object} ErrorResponse "Unauthorized"
// @Failure 429 {object} ErrorResponse "Rate limit exceeded"
// @Failure 500 {object} ErrorResponse "Internal server error"
// @Router /export [get]
func (h *ExportHandlers) ExportData(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("user_id").(string)
query := queries.ExportUserDataQuery{
UserID: userID,
}
result, err := h.exportHandler.Handle(r.Context(), query)
if err != nil {
respondErrorI18n(w, r, h.translator, http.StatusInternalServerError, "export_failed")
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Encoding", "gzip")
w.Header().Set("Content-Disposition", "attachment; filename=\"apocapoc-export.json.gz\"")
w.WriteHeader(http.StatusOK)
gzipWriter := gzip.NewWriter(w)
defer gzipWriter.Close()
encoder := json.NewEncoder(gzipWriter)
encoder.SetIndent("", " ")
if err := encoder.Encode(result); err != nil {
return
}
}