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)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user